LTP GCOV extension - code coverage report
Current view: directory - port - cpl_http.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 165
Code covered: 44.2 % Executed lines: 73

       1                 : /******************************************************************************
       2                 :  * $Id: cpl_http.cpp 19162 2010-03-22 16:45:08Z warmerdam $
       3                 :  *
       4                 :  * Project:  WCS Client Driver
       5                 :  * Purpose:  Implementation of Dataset and RasterBand classes for WCS.
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2006, Frank Warmerdam
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include <map>
      31                 : #include "cpl_http.h"
      32                 : #include "cpl_multiproc.h"
      33                 : 
      34                 : #ifdef HAVE_CURL
      35                 : #  include <curl/curl.h>
      36                 : #endif
      37                 : 
      38                 : CPL_CVSID("$Id: cpl_http.cpp 19162 2010-03-22 16:45:08Z warmerdam $");
      39                 : 
      40                 : // list of named persistent http sessions 
      41                 : 
      42                 : #ifdef HAVE_CURL
      43             896 : static std::map<CPLString,CURL*> oSessionMap;
      44                 : static void *hSessionMapMutex = NULL;
      45                 : #endif
      46                 : 
      47                 : /************************************************************************/
      48                 : /*                            CPLWriteFct()                             */
      49                 : /*                                                                      */
      50                 : /*      Append incoming text to our collection buffer, reallocating     */
      51                 : /*      it larger as needed.                                            */
      52                 : /************************************************************************/
      53                 : 
      54                 : #ifdef HAVE_CURL
      55                 : static size_t 
      56              26 : CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
      57                 : 
      58                 : {
      59              26 :     CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
      60                 :     int  nNewSize;
      61                 : 
      62              26 :     nNewSize = psResult->nDataLen + nmemb*size + 1;
      63              26 :     if( nNewSize > psResult->nDataAlloc )
      64                 :     {
      65              12 :         psResult->nDataAlloc = (int) (nNewSize * 1.25 + 100);
      66                 :         GByte* pabyNewData = (GByte *) VSIRealloc(psResult->pabyData,
      67              12 :                                                   psResult->nDataAlloc);
      68              12 :         if( pabyNewData == NULL )
      69                 :         {
      70               0 :             VSIFree(psResult->pabyData);
      71               0 :             psResult->pabyData = NULL;
      72               0 :             psResult->pszErrBuf = CPLStrdup(CPLString().Printf("Out of memory allocating %d bytes for HTTP data buffer.", psResult->nDataAlloc));
      73               0 :             psResult->nDataAlloc = psResult->nDataLen = 0;
      74                 : 
      75               0 :             return 0;
      76                 :         }
      77              12 :         psResult->pabyData = pabyNewData;
      78                 :     }
      79                 : 
      80                 :     memcpy( psResult->pabyData + psResult->nDataLen, buffer,
      81              26 :             nmemb * size );
      82                 : 
      83              26 :     psResult->nDataLen += nmemb * size;
      84              26 :     psResult->pabyData[psResult->nDataLen] = 0;
      85                 : 
      86              26 :     return nmemb;
      87                 : }
      88                 : 
      89                 : /************************************************************************/
      90                 : /*                           CPLHdrWriteFct()                           */
      91                 : /************************************************************************/
      92              24 : static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
      93                 : {
      94              24 :     CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
      95                 :     // copy the buffer to a char* and initialize with zeros (zero terminate as well)
      96              24 :     char* pszHdr = (char*)CPLCalloc(nmemb + 1, size);
      97              24 :     CPLPrintString(pszHdr, (char *)buffer, nmemb * size);
      98              24 :     char *pszKey = NULL;
      99              24 :     const char *pszValue = CPLParseNameValue(pszHdr, &pszKey );
     100              24 :     psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue);
     101              24 :     CPLFree(pszHdr);
     102              24 :     CPLFree(pszKey);
     103              24 :     return nmemb; 
     104                 : }
     105                 : 
     106                 : #endif /* def HAVE_CURL */
     107                 : 
     108                 : /************************************************************************/
     109                 : /*                           CPLHTTPFetch()                             */
     110                 : /************************************************************************/
     111                 : 
     112                 : /**
     113                 :  * \brief Fetch a document from an url and return in a string.
     114                 :  *
     115                 :  * @param pszURL valid URL recognized by underlying download library (libcurl)
     116                 :  * @param papszOptions option list as a NULL-terminated array of strings. May be NULL.
     117                 :  *                     The following options are handled :
     118                 :  * <ul>
     119                 :  * <li>TIMEOUT=val, where val is in seconds</li>
     120                 :  * <li>HEADERS=val, where val is an extra header to use when getting a web page.
     121                 :  *                  For example "Accept: application/x-ogcwkt"
     122                 :  * <li>HTTPAUTH=[BASIC/NTLM/ANY] to specify an authentication scheme to use.
     123                 :  * <li>USERPWD=userid:password to specify a user and password for authentication
     124                 :  * </ul>
     125                 :  *
     126                 :  * @return a CPLHTTPResult* structure that must be freed by CPLHTTPDestroyResult(),
     127                 :  *         or NULL if libcurl support is diabled
     128                 :  */
     129               3 : CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions )
     130                 : 
     131                 : {
     132                 : #ifndef HAVE_CURL
     133                 :     CPLError( CE_Failure, CPLE_NotSupported,
     134                 :               "GDAL/OGR not compiled with libcurl support, remote requests not supported." );
     135                 :     return NULL;
     136                 : #else
     137                 : /* -------------------------------------------------------------------- */
     138                 : /*      Are we using a persistent named session?  If so, search for     */
     139                 : /*      or create it.                                                   */
     140                 : /*                                                                      */
     141                 : /*      Currently this code does not attempt to protect against         */
     142                 : /*      multiple threads asking for the same named session.  If that    */
     143                 : /*      occurs it will be in use in multiple threads at once which      */
     144                 : /*      might have bad consequences depending on what guarantees        */
     145                 : /*      libcurl gives - which I have not investigated.                  */
     146                 : /* -------------------------------------------------------------------- */
     147               3 :     CURL *http_handle = NULL;
     148                 : 
     149               3 :     const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" );
     150               3 :     if (pszPersistent)
     151                 :     {
     152               0 :         CPLString osSessionName = pszPersistent;
     153               0 :         CPLMutexHolder oHolder( &hSessionMapMutex );
     154                 : 
     155               0 :         if( oSessionMap.count( osSessionName ) == 0 )
     156                 :         {
     157               0 :             oSessionMap[osSessionName] = curl_easy_init();
     158                 :             CPLDebug( "HTTP", "Establish persistent session named '%s'.",
     159               0 :                       osSessionName.c_str() );
     160                 :         }
     161                 : 
     162               0 :         http_handle = oSessionMap[osSessionName];
     163                 :     }
     164                 :     else
     165               3 :         http_handle = curl_easy_init();
     166                 : 
     167                 : /* -------------------------------------------------------------------- */
     168                 : /*      Setup the request.                                              */
     169                 : /* -------------------------------------------------------------------- */
     170                 :     char szCurlErrBuf[CURL_ERROR_SIZE+1];
     171                 :     CPLHTTPResult *psResult;
     172               3 :     struct curl_slist *headers=NULL; 
     173                 : 
     174               3 :     CPLDebug( "HTTP", "Fetch(%s)", pszURL );
     175                 : 
     176               3 :     psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult));
     177                 : 
     178               3 :     curl_easy_setopt(http_handle, CURLOPT_URL, pszURL );
     179                 : 
     180                 :     /* Support control over HTTPAUTH */
     181               3 :     const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" );
     182               3 :     if( pszHttpAuth == NULL )
     183                 :         /* do nothing */;
     184                 : 
     185                 :     /* CURLOPT_HTTPAUTH is defined in curl 7.11.0 or newer */
     186                 : #if LIBCURL_VERSION_NUM >= 0x70B00
     187               0 :     else if( EQUAL(pszHttpAuth,"BASIC") )
     188               0 :         curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
     189               0 :     else if( EQUAL(pszHttpAuth,"NTLM") )
     190               0 :         curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );
     191               0 :     else if( EQUAL(pszHttpAuth,"ANY") )
     192               0 :         curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
     193                 :     else
     194                 :     {
     195                 :         CPLError( CE_Warning, CPLE_AppDefined,
     196                 :                   "Unsupported HTTPAUTH value '%s', ignored.", 
     197               0 :                   pszHttpAuth );
     198                 :     }
     199                 : #else
     200                 :     else
     201                 :     {
     202                 :         CPLError( CE_Warning, CPLE_AppDefined,
     203                 :                   "HTTPAUTH option needs curl >= 7.11.0" );
     204                 :     }
     205                 : #endif
     206                 : 
     207                 :     /* Support setting userid:password */
     208               3 :     const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" );
     209               3 :     if( pszUserPwd != NULL )
     210               0 :         curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd );
     211                 : 
     212                 :     // turn off SSL verification, accept all servers with ssl
     213               3 :     curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
     214                 : 
     215                 :     /* Enable following redirections.  Requires libcurl 7.10.1 at least */
     216               3 :     curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 );
     217               3 :     curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 );
     218                 :     
     219                 :     /* Set timeout.*/
     220               3 :     const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" );
     221               3 :     if( pszTimeout != NULL )
     222                 :         curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, 
     223               2 :                          atoi(pszTimeout) );
     224                 : 
     225                 :     /* Set Headers.*/
     226               3 :     const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" );
     227               3 :     if( pszHeaders != NULL ) {
     228               2 :         CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders);
     229               2 :         headers = curl_slist_append(headers, pszHeaders);
     230               2 :         curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
     231                 :     }
     232                 :                          
     233                 :     /* NOSIGNAL should be set to true for timeout to work in multithread
     234                 :      * environments on Unix, requires libcurl 7.10 or more recent.
     235                 :      * (this force avoiding the use of sgnal handlers)
     236                 :      */
     237                 : #ifdef CURLOPT_NOSIGNAL
     238                 :     curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 );
     239                 : #endif
     240                 : 
     241                 :     // capture response headers
     242               3 :     curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
     243               3 :     curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct);
     244                 :  
     245               3 :     curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult );
     246               3 :     curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct );
     247                 : 
     248               3 :     szCurlErrBuf[0] = '\0';
     249                 : 
     250               3 :     curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf );
     251                 : 
     252                 : /* -------------------------------------------------------------------- */
     253                 : /*      Execute the request, waiting for results.                       */
     254                 : /* -------------------------------------------------------------------- */
     255               3 :     psResult->nStatus = (int) curl_easy_perform( http_handle );
     256                 : 
     257                 : /* -------------------------------------------------------------------- */
     258                 : /*      Fetch content-type if possible.                                 */
     259                 : /* -------------------------------------------------------------------- */
     260                 :     CURLcode err;
     261                 : 
     262               3 :     psResult->pszContentType = NULL;
     263                 :     err = curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE, 
     264               3 :                              &(psResult->pszContentType) );
     265               3 :     if( psResult->pszContentType != NULL )
     266               3 :         psResult->pszContentType = CPLStrdup(psResult->pszContentType);
     267                 : 
     268                 : /* -------------------------------------------------------------------- */
     269                 : /*      Have we encountered some sort of error?                         */
     270                 : /* -------------------------------------------------------------------- */
     271               3 :     if( strlen(szCurlErrBuf) > 0 )
     272                 :     {
     273               0 :         psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
     274                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     275               0 :                   "%s", szCurlErrBuf );
     276                 :     }
     277                 : 
     278               3 :     if (!pszPersistent)
     279               3 :         curl_easy_cleanup( http_handle );
     280                 : 
     281               3 :     curl_slist_free_all(headers);
     282                 : 
     283               3 :     return psResult;
     284                 : #endif /* def HAVE_CURL */
     285                 : }
     286                 : 
     287                 : /************************************************************************/
     288                 : /*                           CPLHTTPEnabled()                           */
     289                 : /************************************************************************/
     290                 : 
     291                 : /**
     292                 :  * \brief Return if CPLHTTP services can be usefull
     293                 :  *
     294                 :  * Those services depend on GDAL being build with libcurl support.
     295                 :  *
     296                 :  * @return TRUE if libcurl support is enabled
     297                 :  */
     298               0 : int CPLHTTPEnabled()
     299                 : 
     300                 : {
     301                 : #ifdef HAVE_CURL
     302               0 :     return TRUE;
     303                 : #else
     304                 :     return FALSE;
     305                 : #endif
     306                 : }
     307                 : 
     308                 : /************************************************************************/
     309                 : /*                           CPLHTTPCleanup()                           */
     310                 : /************************************************************************/
     311                 : 
     312                 : /**
     313                 :  * \brief Cleanup function to call at application termination
     314                 :  */
     315               0 : void CPLHTTPCleanup()
     316                 : 
     317                 : {
     318                 : #ifdef HAVE_CURL
     319               0 :     if( !hSessionMapMutex )
     320               0 :         return;
     321                 : 
     322                 :     {
     323               0 :         CPLMutexHolder oHolder( &hSessionMapMutex );
     324               0 :         std::map<CPLString,CURL*>::iterator oIt;
     325                 : 
     326               0 :         for( oIt=oSessionMap.begin(); oIt != oSessionMap.end(); oIt++ )
     327               0 :             curl_easy_cleanup( oIt->second );
     328                 : 
     329               0 :         oSessionMap.clear();
     330                 :     }
     331                 : 
     332                 :     // not quite a safe sequence. 
     333               0 :     CPLDestroyMutex( hSessionMapMutex );
     334               0 :     hSessionMapMutex = NULL;
     335                 : #endif
     336                 : }
     337                 : 
     338                 : /************************************************************************/
     339                 : /*                        CPLHTTPDestroyResult()                        */
     340                 : /************************************************************************/
     341                 : 
     342                 : /**
     343                 :  * \brief Clean the memory associated with the return value of CPLHTTPFetch()
     344                 :  *
     345                 :  * @param psResult pointer to the return value of CPLHTTPFetch()
     346                 :  */
     347               3 : void CPLHTTPDestroyResult( CPLHTTPResult *psResult )
     348                 : 
     349                 : {
     350               3 :     if( psResult )
     351                 :     {
     352               3 :         CPLFree( psResult->pabyData );
     353               3 :         CPLFree( psResult->pszErrBuf );
     354               3 :         CPLFree( psResult->pszContentType );
     355               3 :         CSLDestroy( psResult->papszHeaders );
     356               3 :         CPLFree( psResult );
     357                 :     }
     358               3 : }
     359                 : 
     360                 : /************************************************************************/
     361                 : /*                     CPLHTTPParseMultipartMime()                      */
     362                 : /************************************************************************/
     363                 : 
     364                 : /**
     365                 :  * \brief Parses a a MIME multipart message
     366                 :  *
     367                 :  * This function will iterate over each part and put it in a separate
     368                 :  * element of the pasMimePart array of the provided psResult structure.
     369                 :  *
     370                 :  * @param psResult pointer to the return value of CPLHTTPFetch()
     371                 :  * @return TRUE if the message contains MIME multipart message.
     372                 :  */
     373               0 : int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult )
     374                 : 
     375                 : {
     376                 : /* -------------------------------------------------------------------- */
     377                 : /*      Is it already done?                                             */
     378                 : /* -------------------------------------------------------------------- */
     379               0 :     if( psResult->nMimePartCount > 0 )
     380               0 :         return TRUE;
     381                 : 
     382                 : /* -------------------------------------------------------------------- */
     383                 : /*      Find the boundary setting in the content type.                  */
     384                 : /* -------------------------------------------------------------------- */
     385               0 :     const char *pszBound = NULL;
     386                 : 
     387               0 :     if( psResult->pszContentType != NULL )
     388               0 :         pszBound = strstr(psResult->pszContentType,"boundary=");
     389                 : 
     390               0 :     if( pszBound == NULL )
     391                 :     {
     392                 :         CPLError( CE_Failure, CPLE_AppDefined,
     393               0 :                   "Unable to parse multi-part mime, no boundary setting." );
     394               0 :         return FALSE;
     395                 :     }
     396                 : 
     397               0 :     CPLString osBoundary;
     398                 :     char **papszTokens = 
     399                 :         CSLTokenizeStringComplex( pszBound + 9, "\n ;", 
     400               0 :                                   TRUE, FALSE );
     401                 : 
     402               0 :     if( CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0 )
     403                 :     {
     404                 :         CPLError( CE_Failure, CPLE_AppDefined,
     405               0 :                   "Unable to parse multi-part mime, boundary not parsable." );
     406               0 :         return FALSE;
     407                 :     }
     408                 :     
     409               0 :     osBoundary = "--";
     410               0 :     osBoundary += papszTokens[0];
     411               0 :     CSLDestroy( papszTokens );
     412                 : 
     413                 : /* -------------------------------------------------------------------- */
     414                 : /*      Find the start of the first chunk.                              */
     415                 : /* -------------------------------------------------------------------- */
     416                 :     char *pszNext;
     417                 :     pszNext = (char *) 
     418               0 :         strstr((const char *) psResult->pabyData,osBoundary.c_str());
     419                 :     
     420               0 :     if( pszNext == NULL )
     421                 :     {
     422               0 :         CPLError( CE_Failure, CPLE_AppDefined, "No parts found." );
     423               0 :         return FALSE;
     424                 :     }
     425                 : 
     426               0 :     pszNext += strlen(osBoundary);
     427               0 :     while( *pszNext != '\n' && *pszNext != '\0' )
     428               0 :         pszNext++;
     429               0 :     if( *pszNext == '\n' )
     430               0 :         pszNext++;
     431                 : 
     432                 : /* -------------------------------------------------------------------- */
     433                 : /*      Loop over parts...                                              */
     434                 : /* -------------------------------------------------------------------- */
     435               0 :     while( TRUE )
     436                 :     {
     437               0 :         psResult->nMimePartCount++;
     438                 :         psResult->pasMimePart = (CPLMimePart *)
     439                 :             CPLRealloc(psResult->pasMimePart,
     440               0 :                        sizeof(CPLMimePart) * psResult->nMimePartCount );
     441                 : 
     442               0 :         CPLMimePart *psPart = psResult->pasMimePart+psResult->nMimePartCount-1;
     443                 : 
     444               0 :         memset( psPart, 0, sizeof(CPLMimePart) );
     445                 : 
     446                 : /* -------------------------------------------------------------------- */
     447                 : /*      Collect headers.                                                */
     448                 : /* -------------------------------------------------------------------- */
     449               0 :         while( *pszNext != '\n' && *pszNext != '\0' )
     450                 :         {
     451               0 :             char *pszEOL = strstr(pszNext,"\n");
     452                 : 
     453               0 :             if( pszEOL == NULL )
     454                 :             {
     455               0 :                 CPLAssert( FALSE );
     456               0 :                 break;
     457                 :             }
     458                 : 
     459               0 :             *pszEOL = '\0';
     460                 :             psPart->papszHeaders = 
     461               0 :                 CSLAddString( psPart->papszHeaders, pszNext );
     462               0 :             *pszEOL = '\n';
     463                 :             
     464               0 :             pszNext = pszEOL + 1;
     465                 :         }
     466                 : 
     467               0 :         if( *pszNext == '\n' )
     468               0 :             pszNext++;
     469                 :             
     470                 : /* -------------------------------------------------------------------- */
     471                 : /*      Work out the data block size.                                   */
     472                 : /* -------------------------------------------------------------------- */
     473               0 :         psPart->pabyData = (GByte *) pszNext;
     474                 : 
     475                 :         int nBytesAvail = psResult->nDataLen - 
     476               0 :             (pszNext - (const char *) psResult->pabyData);
     477                 : 
     478               0 :         while( nBytesAvail > 0
     479                 :                && (*pszNext != '-' 
     480                 :                    || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) )
     481                 :         {
     482               0 :             pszNext++;
     483               0 :             nBytesAvail--;
     484                 :         }
     485                 :         
     486               0 :         if( nBytesAvail == 0 )
     487                 :         {
     488               0 :             CPLAssert( FALSE );
     489               0 :             break;
     490                 :         }
     491                 : 
     492               0 :         psPart->nDataLen = pszNext - (const char *) psPart->pabyData;
     493               0 :         pszNext += strlen(osBoundary);
     494                 : 
     495               0 :         if( strncmp(pszNext,"--",2) == 0 )
     496                 :         {
     497               0 :             break;
     498                 :         }
     499               0 :         else if( *pszNext == '\n' )
     500               0 :             pszNext++;
     501                 :         else
     502                 :         {
     503               0 :             CPLAssert( FALSE );
     504               0 :             break;
     505                 :         }
     506                 :     }
     507                 : 
     508               0 :     return TRUE;
     509             896 : }

Generated by: LTP GCOV extension version 1.5