示例#1
0
/**
 * Retrieves the details for the specified location
 *
 * @param pczLocation The string containing the name/code of the location
 *
 * @return A pointer to a list of LocationInfo entries, possibly empty, 
 *         if no details were found. Caller is responsible for freeing the list.
 */
GList *
getLocationInfo(const gchar * pczLocation)
{
  gint iRetCode = 0;
  gint iDataSize = 0;

  GList * pList = NULL;

  gchar * pcEscapedLocation = convertToASCII(pczLocation); 

  gsize len = getWOEIDQueryLength(pcEscapedLocation);

  gchar * cQueryBuffer = g_malloc0(len);

  gint iRet = getWOEIDQuery(cQueryBuffer, pcEscapedLocation);

  g_free(pcEscapedLocation);

  LXW_LOG(LXW_DEBUG, "yahooutil::getLocationInfo(%s): query[%d]: %s",
          pczLocation, iRet, cQueryBuffer);

  gpointer pResponse = getURL(cQueryBuffer, &iRetCode, &iDataSize);

  if (!pResponse || iRetCode != HTTP_STATUS_OK)
    {
      LXW_LOG(LXW_ERROR, "yahooutil::getLocationInfo(%s): Failed with error code %d",
              pczLocation, iRetCode);
    }
  else
    {
      LXW_LOG(LXW_DEBUG, "yahooutil::getLocationInfo(%s): Response code: %d, size: %d",
              pczLocation, iRetCode, iDataSize);

      LXW_LOG(LXW_VERBOSE, "yahooutil::getLocation(%s): Contents: %s", 
              pczLocation, (const char *)pResponse);

      iRet = parseResponse(pResponse, &pList, NULL);
      
      LXW_LOG(LXW_DEBUG, "yahooutil::getLocation(%s): Response parsing returned %d",
              pczLocation, iRet);

      if (iRet)
        {
          // failure
          g_list_free_full(pList, freeLocation);
        }

    }

  g_free(cQueryBuffer);
  g_free(pResponse);

  return pList;
}
示例#2
0
/**
 * Retrieves the forecast for the specified location WOEID
 *
 * @param pczWOEID The string containing the WOEID of the location
 * @param czUnits The character containing the units for the forecast (c|f)
 * @param pForecast The pointer to the forecast to be filled. If set to NULL,
 *                  a new one will be allocated.
 *
 */
void
getForecastInfo(const gchar * pczWOEID, const gchar czUnits, gpointer * pForecast)
{
  gint iRetCode = 0;
  gint iDataSize = 0;

  gsize len = getForecastQueryLength(pczWOEID);

  gchar * cQueryBuffer = g_malloc(len + 1);

  gint iRet = getForecastQuery(cQueryBuffer, pczWOEID, czUnits);

  LXW_LOG(LXW_DEBUG, "yahooutil::getForecastInfo(%s): query[%d]: %s",
          pczWOEID, iRet, cQueryBuffer);

  gpointer pResponse = getURL(cQueryBuffer, &iRetCode, &iDataSize);

  if (!pResponse || iRetCode != HTTP_STATUS_OK)
    {
      LXW_LOG(LXW_ERROR, "yahooutil::getForecastInfo(%s): Failed with error code %d",
              pczWOEID, iRetCode);
    }
  else
    {
      LXW_LOG(LXW_DEBUG, "yahooutil::getForecastInfo(%s): Response code: %d, size: %d",
              pczWOEID, iRetCode, iDataSize);

      LXW_LOG(LXW_VERBOSE, "yahooutil::getForecastInfo(%s): Contents: %s",
              pczWOEID, (const char *)pResponse);

      iRet = parseResponse(pResponse, NULL, pForecast);
    
      LXW_LOG(LXW_DEBUG, "yahooutil::getForecastInfo(%s): Response parsing returned %d",
              pczWOEID, iRet);

      if (iRet)
        {
          freeForecast(*pForecast);

          *pForecast = NULL;
        }

    }

  g_free(cQueryBuffer);
  g_free(pResponse);
}
示例#3
0
/**
 * Converts the passed-in string from UTF-8 to ASCII for http transmisison.
 *
 * @param pczInString String to convert
 *
 * @return The converted string which MUST BE FREED BY THE CALLER.
 */
static gchar *
convertToASCII(const gchar *pczInString)
{
  // for UTF-8 to ASCII conversions
  setlocale(LC_CTYPE, "en_US");

  GError * pError = NULL;

  gsize szBytesRead = 0;
  gsize szBytesWritten = 0;

  gchar * pcConvertedString = g_convert(pczInString,
                                        strlen(pczInString),
                                        "ASCII//TRANSLIT",
                                        "UTF-8",
                                        &szBytesRead,
                                        &szBytesWritten,
                                        &pError);

  if (pError)
    {
      LXW_LOG(LXW_ERROR, "yahooutil::convertToASCII(%s): Error: %s", 
              pczInString, pError->message);

      g_error_free(pError);

      pcConvertedString = g_strndup(pczInString, strlen(pczInString));
    }

  // now escape space, if any
  xmlChar * pxEscapedString = xmlURIEscapeStr((const xmlChar *)pcConvertedString, NULL);

  if (pxEscapedString)
    {
      // release ConvertedString, reset it, then release EscapedString.
      // I know it's confusing, but keeps everything as a gchar and g_free
      g_free(pcConvertedString);

      pcConvertedString = g_strndup((const gchar *)pxEscapedString, 
                                    strlen((const gchar *)pxEscapedString));

      xmlFree(pxEscapedString);
    }

  // restore locale to default
  setlocale(LC_CTYPE, "");

  return pcConvertedString;
}
示例#4
0
/**
 * Parses the response and fills in the supplied list with entries (if any)
 *
 * @param pResponse Pointer to the response received.
 * @param pList Pointer to the pointer to the list to populate.
 * @param pForecast Pointer to the pointer to the forecast to retrieve.
 *
 * @return 0 on success, -1 on failure
 *
 * @note If the pList pointer is NULL or the pForecast pointer is NULL,
 *       nothing is done and failure is returned. Otherwise, the appropriate
 *       pointer is set based on the name of the XML element:
 *       'Result' for GList (pList)
 *       'channel' for Forecast (pForecast)
 */
static gint
parseResponse(gpointer pResponse, GList ** pList, gpointer * pForecast)
{
  int iLocation = (pList)?1:0;

  xmlDocPtr pDoc = xmlReadMemory(CONSTCHAR_P(pResponse),
                                 strlen(pResponse),
                                 "",
                                 NULL,
                                 0);

  if (!pDoc)
    {
      // failed
      LXW_LOG(LXW_ERROR, "yahooutil::parseResponse(): Failed to parse response %s",
              CONSTCHAR_P(pResponse));

      return -1;
    }

  xmlNodePtr pRoot = xmlDocGetRootElement(pDoc);
  
  // the second part of the if can be broken out
  if (!pRoot || !xmlStrEqual(pRoot->name, CONSTXMLCHAR_P("query")))
    {
      // failed
      LXW_LOG(LXW_ERROR, "yahooutil::parseResponse(): Failed to retrieve root %s",
              CONSTCHAR_P(pResponse));

      xmlFreeDoc(pDoc);

      return -1;
    }

  // use xpath to find /query/results/Result
  xmlXPathInit();

  xmlXPathContextPtr pXCtxt = xmlXPathNewContext(pDoc);

  const char * pczExpression = "/query/results/channel";

  if (iLocation)
    {
      xmlXPathRegisterNs(pXCtxt, CONSTXMLCHAR_P("ns1"),
                         CONSTXMLCHAR_P("http://where.yahooapis.com/v1/schema.rng"));

      pczExpression = "///ns1:place";
    }

  // have some results...
  xmlNodeSetPtr pNodeSet = evaluateXPathExpression(pXCtxt, pczExpression);

  if (!pNodeSet)
    {
      // error, or no results found -- failed
      xmlXPathFreeContext(pXCtxt);

      xmlFreeDoc(pDoc);

      return -1;
    }

  int iCount = 0;
  int iSize = pNodeSet->nodeNr;

  gint iRetVal = 0;

  for (; iCount < iSize; ++iCount)
    {
      if (pNodeSet->nodeTab)
        {
          xmlNodePtr pNode = pNodeSet->nodeTab[iCount];

          if (pNode && pNode->type == XML_ELEMENT_NODE)
            {
              if (xmlStrEqual(pNode->name, CONSTXMLCHAR_P("place")))
                {
                  gpointer pEntry = processResultNode(pNode);
                  
                  if (pEntry && pList)
                    {
                      *pList = g_list_prepend(*pList, pEntry);
                    }
                }
              else if (xmlStrEqual(pNode->name, CONSTXMLCHAR_P("channel")))
                {
                  ForecastInfo * pEntry = NULL;
                  
                  gboolean bNewed = FALSE;

                  /* Check if forecast is allocated, if not, 
                   * allocate and populate 
                   */
                  if (pForecast)
                    {
                      if (*pForecast)
                        {
                          pEntry = (ForecastInfo *)*pForecast;
                        }
                      else
                        {
                          pEntry = (ForecastInfo *)g_try_new0(ForecastInfo, 1);

                          bNewed = TRUE;
                        }
                  
                      if (!pEntry)
                        {
                          iRetVal = -1;
                        }
                      else
                        {
                          *pForecast = processChannelNode(pNode, pEntry);
                          
                          if (!*pForecast)
                            {
                              /* Failed, forecast is freed by caller */
                              
                              /* Unless it was just newed... */
                              if (bNewed)
                                {
                                  g_free(pEntry);
                                }
                          
                              iRetVal = -1;
                            }
                        }

                    }// end else if pForecast

                }// end else if 'channel'

            }// end if element

        }// end if nodeTab

    }// end for noteTab size

  xmlXPathFreeNodeSet(pNodeSet);

  xmlXPathFreeContext(pXCtxt);

  xmlFreeDoc(pDoc);

  return iRetVal;
}
示例#5
0
/**
 * Processes the passed-in node to generate a ForecastInfo entry
 *
 * @param pNode Pointer to the XML Channel Node.
 * @param pEntry Pointer to the ForecastInfo entry to be filled in.
 *
 * @return A newly created ForecastInfo entry on success, or NULL on failure.
 */
static gpointer
processChannelNode(xmlNodePtr pNode, ForecastInfo * pEntry)
{
  if (!pNode)
    {
      return NULL;
    }

  xmlNodePtr pCurr = pNode->xmlChildrenNode;

  for (; pCurr != NULL; pCurr = pCurr->next)
    {
      if (pCurr->type == XML_ELEMENT_NODE)
        {
          if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("title")))
            {
              /* Evaluate title to see if there was an error */
              char * pcContent = CHAR_P(xmlNodeListGetString(pCurr->doc, 
                                                             pCurr->xmlChildrenNode, 
                                                             1));
              
              if (strstr(pcContent, "Error"))
                {
                  xmlFree(XMLCHAR_P(pcContent));
                  
                  do
                    {
                      pCurr = pCurr->next;
                    } while (pCurr && !xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("item")));

                  xmlNodePtr pChild = (pCurr)?pCurr->xmlChildrenNode:NULL;
                  
                  for (; pChild != NULL; pChild = pChild->next)
                    {
                      if (pChild->type == XML_ELEMENT_NODE && 
                          xmlStrEqual(pChild->name, CONSTXMLCHAR_P("title")))
                        {
                          pcContent = CHAR_P(xmlNodeListGetString(pChild->doc, 
                                                                  pChild->xmlChildrenNode, 
                                                                  1));

                          LXW_LOG(LXW_ERROR, "yahooutil::processChannelNode(): Forecast retrieval error: %s",
                                  pcContent);


                          xmlFree(XMLCHAR_P(pcContent));
                        }
                    }

                  return NULL;
                }
              else
                {
                  xmlFree(XMLCHAR_P(pcContent));
                  /* ...and continue... */
                }

            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("item")))
            {
              /* item child element gets 'special' treatment */
              processItemNode((gpointer *)&pEntry, pCurr);
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("units")))
            {
              // distance
              const char * pczDistance = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("distance")));

              gsize distanceLength = ((pczDistance)?strlen(pczDistance):0);

              setStringIfDifferent(&pEntry->units_.pcDistance_, pczDistance, distanceLength);

              xmlFree(XMLCHAR_P(pczDistance));

              // pressure
              const char * pczPressure = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("pressure")));

              gsize pressureLength = ((pczPressure)?strlen(pczPressure):0);

              setStringIfDifferent(&pEntry->units_.pcPressure_, pczPressure, pressureLength);

              xmlFree(XMLCHAR_P(pczPressure));

              // speed
              const char * pczSpeed = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("speed")));

              gsize speedLength = ((pczSpeed)?strlen(pczSpeed):0);

              setStringIfDifferent(&pEntry->units_.pcSpeed_, pczSpeed, speedLength);

              xmlFree(XMLCHAR_P(pczSpeed));

              // temperature
              const char * pczTemperature = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("temperature")));

              gsize temperatureLength = ((pczTemperature)?strlen(pczTemperature):0);

              setStringIfDifferent(&pEntry->units_.pcTemperature_, pczTemperature, temperatureLength);

              xmlFree(XMLCHAR_P(pczTemperature));
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("wind")))
            {
              // chill
              const char * pczChill = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("chill")));

              setIntIfDifferent(&pEntry->iWindChill_, pczChill);

              xmlFree(XMLCHAR_P(pczChill));

              // direction
              const char * pczDirection = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("direction")));

              gint iValue = (gint)g_ascii_strtoll((pczDirection)?pczDirection:"999", NULL, 10);

              const gchar * pczDir = WIND_DIRECTION(iValue);

              setStringIfDifferent(&pEntry->pcWindDirection_, pczDir, strlen(pczDir));

              xmlFree(XMLCHAR_P(pczDirection));

              // speed
              const char * pczSpeed = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("speed")));

              setIntIfDifferent(&pEntry->iWindSpeed_, pczSpeed);

              xmlFree(XMLCHAR_P(pczSpeed));
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("atmosphere")))
            {
              // humidity
              const char * pczHumidity = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("humidity")));

              setIntIfDifferent(&pEntry->iHumidity_, pczHumidity);

              xmlFree(XMLCHAR_P(pczHumidity));

              // pressure
              const char * pczPressure = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("pressure")));

              pEntry->dPressure_ = g_ascii_strtod((pczPressure)?pczPressure:"0", NULL);

              xmlFree(XMLCHAR_P(pczPressure));

              // visibility
              const char * pczVisibility = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("visibility")));

              pEntry->dVisibility_ = g_ascii_strtod((pczVisibility)?pczVisibility:"0", NULL);

              // need to divide by 100
              //pEntry->dVisibility_ = pEntry->dVisibility_/100;

              xmlFree(XMLCHAR_P(pczVisibility));

              // state
              const char * pczState = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("rising")));
                            
              pEntry->pressureState_ = (PressureState)g_ascii_strtoll((pczState)?pczState:"0", NULL, 10);

              xmlFree(XMLCHAR_P(pczState));
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("astronomy")))
            {
              // sunrise
              const char * pczSunrise = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("sunrise")));

              gsize sunriseLength = ((pczSunrise)?strlen(pczSunrise):0);

              setStringIfDifferent(&pEntry->pcSunrise_, pczSunrise, sunriseLength);

              xmlFree(XMLCHAR_P(pczSunrise));

              // sunset
              const char * pczSunset = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("sunset")));

              gsize sunsetLength = ((pczSunset)?strlen(pczSunset):0);

              setStringIfDifferent(&pEntry->pcSunset_, pczSunset, sunsetLength);

              xmlFree(XMLCHAR_P(pczSunset));
            }
          
        }

    }

  return pEntry;
}
示例#6
0
/**
 * Processes the passed-in node to generate a LocationInfo entry
 *
 * @param pEntry Pointer to the pointer to the ForecastInfo entry being filled.
 * @param pNode Pointer to the XML Item Node.
 *
 * @return 0 on success, -1 on failure
 */
static gint
processItemNode(gpointer * pEntry, xmlNodePtr pNode)
{
  if (!pNode || !pEntry)
    {
      return -1;
    }

  ForecastInfo * pInfo = *((ForecastInfo **)pEntry);

  xmlNodePtr pCurr = pNode->xmlChildrenNode;

  int iForecastCount = 0;

  for (; pCurr != NULL; pCurr = pCurr->next)
    {
      if (pCurr->type == XML_ELEMENT_NODE)
        {
          if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("condition")))
            {
              const char * pczDate = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("date")));
              const char * pczTemp = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("temp")));
              const char * pczText = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("text")));

              gsize dateLength = ((pczDate)?strlen(pczDate):0);
              gsize textLength = ((pczText)?strlen(pczText):0);

              setStringIfDifferent(&pInfo->pcTime_, pczDate, dateLength);

              setIntIfDifferent(&pInfo->iTemperature_, pczTemp);

              setStringIfDifferent(&pInfo->pcConditions_, pczText, textLength);

              xmlFree(XMLCHAR_P(pczDate));
              xmlFree(XMLCHAR_P(pczTemp));
              xmlFree(XMLCHAR_P(pczText));
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("description")))
            {
              char * pcContent = CHAR_P(xmlNodeListGetString(pCurr->doc, 
                                                             pCurr->xmlChildrenNode, 
                                                             1));
          
              char * pcSavePtr = NULL;

              // initial call to find the first '"'
              strtok_r(pcContent, "\"", &pcSavePtr);

              // second call to find the second '"'
              char * pcImageURL = strtok_r(NULL, "\"", &pcSavePtr);

              // found the image
              if (pcImageURL && strstr(pcImageURL, "yimg.com"))
                {
                  LXW_LOG(LXW_DEBUG, "yahooutil::processItemNode(): IMG URL: %s",
                          pcImageURL);

                  setImageIfDifferent(&pInfo->pcImageURL_, 
                                      &pInfo->pImage_,
                                      pcImageURL, 
                                      strlen(pcImageURL));
                }
                  
              xmlFree(XMLCHAR_P(pcContent));
            }
          else if (xmlStrEqual(pCurr->name, CONSTXMLCHAR_P("forecast")))
            {
              ++iForecastCount;

              const char * pczDay = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("day")));
              const char * pczHigh = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("high")));
              const char * pczLow = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("low")));
              const char * pczText = CONSTCHAR_P(xmlGetProp(pCurr, XMLCHAR_P("text")));

              gsize dayLength = ((pczDay)?strlen(pczDay):0);
              gsize textLength = ((pczText)?strlen(pczText):0);

              if (iForecastCount == 1)
                {
                  setStringIfDifferent(&pInfo->today_.pcDay_, pczDay, dayLength);

                  setIntIfDifferent(&pInfo->today_.iHigh_, pczHigh);

                  setIntIfDifferent(&pInfo->today_.iLow_, pczLow);

                  setStringIfDifferent(&pInfo->today_.pcConditions_, pczText, textLength);
                }
              else
                {
                  setStringIfDifferent(&pInfo->tomorrow_.pcDay_, pczDay, dayLength);

                  setIntIfDifferent(&pInfo->tomorrow_.iHigh_, pczHigh);

                  setIntIfDifferent(&pInfo->tomorrow_.iLow_, pczLow);

                  setStringIfDifferent(&pInfo->tomorrow_.pcConditions_, pczText, textLength);
                }

              xmlFree(XMLCHAR_P(pczDay));
              xmlFree(XMLCHAR_P(pczHigh));
              xmlFree(XMLCHAR_P(pczLow));
              xmlFree(XMLCHAR_P(pczText));
            }

        }

    }

  return 0;
}
示例#7
0
/**
 * Compares the URL of an image to the 'new' value. If the two
 * are different, the image at the 'new' URL is retrieved and replaces
 * the old one. The old one is freed.
 *
 * @param pcStorage Pointer to the storage location with the first value.
 * @param pImage Pointer to the image storage.
 * @param pczNewURL The new url.
 * @param szURLLength The length of the new URL.
 *
 * @return 0 on succes, -1 on failure.
 */
static gint
setImageIfDifferent(gchar ** pcStorage,
                    GdkPixbuf ** pImage,
                    const gchar * pczNewURL,
                    const gsize szURLLength)
{
  int err = 0;

  // if diffrent, clear and set
  if (g_strcmp0(*pcStorage, pczNewURL))
    {
      g_free(*pcStorage);

      *pcStorage = g_strndup(pczNewURL, szURLLength);

      if (*pImage)
        {
          g_object_unref(*pImage);

          *pImage = NULL;
        }
      
      // retrieve the URL and create the new image
      gint iRetCode = 0;
      gint iDataSize = 0;

      gpointer pResponse = getURL(pczNewURL, &iRetCode, &iDataSize);

      if (!pResponse || iRetCode != HTTP_STATUS_OK)
        {
          LXW_LOG(LXW_ERROR, "yahooutil::setImageIfDifferent(): Failed to get URL (%d, %d)", 
                  iRetCode, iDataSize);

          return -1;
        }

      GInputStream * pInputStream = g_memory_input_stream_new_from_data(pResponse,
                                                                        iDataSize,
                                                                        g_free);

      GError * pError = NULL;

      *pImage = gdk_pixbuf_new_from_stream(pInputStream,
                                           NULL,
                                           &pError);

      if (!*pImage)
        {
          LXW_LOG(LXW_ERROR, "yahooutil::setImageIfDifferent(): PixBuff allocation failed: %s",
                  pError->message);

          g_error_free(pError);
          
          err = -1;
        }

      if (!g_input_stream_close(pInputStream, NULL, &pError))
        {
          LXW_LOG(LXW_ERROR, "yahooutil::setImageIfDifferent(): InputStream closure failed: %s",
                  pError->message);

          g_error_free(pError);

          err = -1;
        }
      
    }

  return err;
}
示例#8
0
/**
 * Prints the contents of the supplied entry to stdout
 *
 * @param pEntry Entry contents of which to print.
 *
 */
void
printForecast(gpointer pEntry G_GNUC_UNUSED)
{
#ifdef DEBUG
  if (!pEntry)
    {
      LXW_LOG(LXW_ERROR, "forecast::printForecast(): Entry: NULL");
      
      return;
    }
  
  ForecastInfo * pInfo = (ForecastInfo *)pEntry;
  
  LXW_LOG(LXW_VERBOSE, "Forecast at %s:", (const char *)pInfo->pcTime_);
  LXW_LOG(LXW_VERBOSE, "\tTemperature: %d%s", 
          pInfo->iTemperature_,
          (const char *)pInfo->units_.pcTemperature_);
  LXW_LOG(LXW_VERBOSE, "\tHumidity: %d%s", pInfo->iHumidity_, "%");
  LXW_LOG(LXW_VERBOSE, "\tWind chill: %d%s, speed: %d%s, direction %s", 
          pInfo->iWindChill_,
          (const char *)pInfo->units_.pcTemperature_,
          pInfo->iWindSpeed_,
          (const char *)pInfo->units_.pcSpeed_,
          pInfo->pcWindDirection_);
  LXW_LOG(LXW_VERBOSE, "\tPressure: %2.02f%s and %s", 
          pInfo->dPressure_,
          (const char *)pInfo->units_.pcPressure_,
          ((pInfo->pressureState_ == STEADY)?"steady":
           (pInfo->pressureState_ == RISING)?"rising":
           (pInfo->pressureState_ == FALLING)?"falling":"?"));
  LXW_LOG(LXW_VERBOSE, "\tConditions: %s", (const char *)pInfo->pcConditions_);
  LXW_LOG(LXW_VERBOSE, "\tVisibility: %3.02f%s", 
          pInfo->dVisibility_,
          (const char *)pInfo->units_.pcDistance_);
  LXW_LOG(LXW_VERBOSE, "\tSunrise: %s", (const char *)pInfo->pcSunrise_);
  LXW_LOG(LXW_VERBOSE, "\tSunset: %s", (const char *)pInfo->pcSunset_);
  LXW_LOG(LXW_VERBOSE, "\tImage URL: %s", pInfo->pcImageURL_);

  LXW_LOG(LXW_VERBOSE, "\tTwo-day forecast:");
  LXW_LOG(LXW_VERBOSE, "\t\t%s: High: %d%s, Low: %d%s, Conditions: %s",
          (const char *)pInfo->today_.pcDay_,
          pInfo->today_.iHigh_,
          (const char *)pInfo->units_.pcTemperature_,
          pInfo->today_.iLow_,
          (const char *)pInfo->units_.pcTemperature_,
          (const char *)pInfo->today_.pcConditions_);
  LXW_LOG(LXW_VERBOSE, "\t\t%s: High: %d%s, Low: %d%s, Conditions: %s",
          (const char *)pInfo->tomorrow_.pcDay_,
          pInfo->tomorrow_.iHigh_,
          (const char *)pInfo->units_.pcTemperature_,
          pInfo->tomorrow_.iLow_,
          (const char *)pInfo->units_.pcTemperature_,
          (const char *)pInfo->tomorrow_.pcConditions_);
#endif
}