1 /******************************************************************************
2  *
3  * Project:  CPL - Common Portability Library
4  * Purpose:  Generic data file location finder, with application hooking.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2000, Frank Warmerdam
9  * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
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 "cpl_port.h"
31 #include "cpl_conv.h"
32 
33 #include <cstddef>
34 
35 #include "cpl_multiproc.h"
36 #include "cpl_string.h"
37 #include "cpl_vsi.h"
38 
39 CPL_CVSID("$Id: cpl_findfile.cpp 36a6a8ceffd622ab2b2ecddf489b5f790f1c65a0 2019-08-12 23:56:02 +0200 Even Rouault $")
40 
41 typedef struct
42 {
43     bool bFinderInitialized;
44     int nFileFinders;
45     CPLFileFinder *papfnFinders;
46     char **papszFinderLocations;
47 } FindFileTLS;
48 
49 /************************************************************************/
50 /*                      CPLFindFileDeinitTLS()                          */
51 /************************************************************************/
52 
53 static void CPLPopFinderLocationInternal( FindFileTLS* pTLSData );
54 static CPLFileFinder CPLPopFileFinderInternal( FindFileTLS* pTLSData );
55 
CPLFindFileFreeTLS(void * pData)56 static void CPLFindFileFreeTLS( void* pData )
57 {
58     FindFileTLS* pTLSData = reinterpret_cast<FindFileTLS *>( pData );
59     if( pTLSData != nullptr && pTLSData->bFinderInitialized )
60     {
61         while( pTLSData->papszFinderLocations != nullptr )
62             CPLPopFinderLocationInternal(pTLSData);
63         while( CPLPopFileFinderInternal(pTLSData) != nullptr ) {}
64 
65         pTLSData->bFinderInitialized = false;
66     }
67     CPLFree(pTLSData);
68 }
69 
70 /************************************************************************/
71 /*                       CPLGetFindFileTLS()                            */
72 /************************************************************************/
73 
CPLGetFindFileTLS()74 static FindFileTLS* CPLGetFindFileTLS()
75 {
76     int bMemoryError = FALSE;
77     FindFileTLS* pTLSData =
78         reinterpret_cast<FindFileTLS *>(
79             CPLGetTLSEx( CTLS_FINDFILE, &bMemoryError ) );
80     if( bMemoryError )
81         return nullptr;
82     if( pTLSData == nullptr )
83     {
84         pTLSData = static_cast<FindFileTLS *>(
85             VSI_CALLOC_VERBOSE(1, sizeof(FindFileTLS) ) );
86         if( pTLSData == nullptr )
87             return nullptr;
88         CPLSetTLSWithFreeFunc( CTLS_FINDFILE, pTLSData, CPLFindFileFreeTLS );
89     }
90     return pTLSData;
91 }
92 
93 /************************************************************************/
94 /*                           CPLFinderInit()                            */
95 /************************************************************************/
96 
CPLFinderInit()97 static FindFileTLS* CPLFinderInit()
98 
99 {
100     FindFileTLS* pTLSData = CPLGetFindFileTLS();
101     if( pTLSData != nullptr && !pTLSData->bFinderInitialized )
102     {
103         pTLSData->bFinderInitialized = true;
104         CPLPushFileFinder( CPLDefaultFindFile );
105 
106         CPLPushFinderLocation( "." );
107 
108         if( CPLGetConfigOption( "GDAL_DATA", nullptr ) != nullptr )
109         {
110             CPLPushFinderLocation( CPLGetConfigOption( "GDAL_DATA", nullptr ) );
111         }
112         else
113         {
114 #ifdef INST_DATA
115             CPLPushFinderLocation( INST_DATA );
116 #endif
117 #ifdef GDAL_PREFIX
118   #ifdef MACOSX_FRAMEWORK
119             CPLPushFinderLocation( GDAL_PREFIX "/Resources/gdal" );
120   #else
121             CPLPushFinderLocation( GDAL_PREFIX "/share/gdal" );
122   #endif
123 #endif
124         }
125     }
126     return pTLSData;
127 }
128 
129 /************************************************************************/
130 /*                           CPLFinderClean()                           */
131 /************************************************************************/
132 
133 /** CPLFinderClean */
CPLFinderClean()134 void CPLFinderClean()
135 
136 {
137     FindFileTLS* pTLSData = CPLGetFindFileTLS();
138     CPLFindFileFreeTLS(pTLSData);
139     int bMemoryError = FALSE;
140     CPLSetTLSWithFreeFuncEx( CTLS_FINDFILE, nullptr, nullptr, &bMemoryError );
141     // TODO: if( bMemoryError ) {}
142 }
143 
144 /************************************************************************/
145 /*                         CPLDefaultFindFile()                         */
146 /************************************************************************/
147 
148 /** CPLDefaultFindFile */
CPLDefaultFindFile(const char *,const char * pszBasename)149 const char *CPLDefaultFindFile( const char * /* pszClass */,
150                                 const char *pszBasename )
151 
152 {
153     FindFileTLS* pTLSData = CPLGetFindFileTLS();
154     if( pTLSData == nullptr )
155         return nullptr;
156     const int nLocations = CSLCount( pTLSData->papszFinderLocations );
157 
158     for( int i = nLocations-1; i >= 0; i-- )
159     {
160         const char *pszResult =
161             CPLFormFilename( pTLSData->papszFinderLocations[i], pszBasename,
162                              nullptr );
163 
164         VSIStatBufL sStat;
165         if( VSIStatL( pszResult, &sStat ) == 0 )
166             return pszResult;
167     }
168 
169     return nullptr;
170 }
171 
172 /************************************************************************/
173 /*                            CPLFindFile()                             */
174 /************************************************************************/
175 
176 /** CPLFindFile */
CPLFindFile(const char * pszClass,const char * pszBasename)177 const char *CPLFindFile( const char *pszClass, const char *pszBasename )
178 
179 {
180     FindFileTLS* pTLSData = CPLFinderInit();
181     if( pTLSData == nullptr )
182         return nullptr;
183 
184     for( int i = pTLSData->nFileFinders-1; i >= 0; i-- )
185     {
186         const char * pszResult =
187             (pTLSData->papfnFinders[i])( pszClass, pszBasename );
188         if( pszResult != nullptr )
189             return pszResult;
190     }
191 
192     return nullptr;
193 }
194 
195 /************************************************************************/
196 /*                         CPLPushFileFinder()                          */
197 /************************************************************************/
198 
199 /** CPLPushFileFinder */
CPLPushFileFinder(CPLFileFinder pfnFinder)200 void CPLPushFileFinder( CPLFileFinder pfnFinder )
201 
202 {
203     FindFileTLS* pTLSData = CPLFinderInit();
204     if( pTLSData == nullptr )
205         return;
206 
207     pTLSData->papfnFinders = static_cast<CPLFileFinder *>(
208         CPLRealloc(pTLSData->papfnFinders,
209             sizeof(CPLFileFinder) * ++pTLSData->nFileFinders) );
210     pTLSData->papfnFinders[pTLSData->nFileFinders-1] = pfnFinder;
211 }
212 
213 /************************************************************************/
214 /*                          CPLPopFileFinder()                          */
215 /************************************************************************/
216 
CPLPopFileFinderInternal(FindFileTLS * pTLSData)217 CPLFileFinder CPLPopFileFinderInternal( FindFileTLS* pTLSData )
218 
219 {
220     if( pTLSData == nullptr )
221         return nullptr;
222     if( pTLSData->nFileFinders == 0 )
223         return nullptr;
224 
225     CPLFileFinder pfnReturn = pTLSData->papfnFinders[--pTLSData->nFileFinders];
226 
227     if( pTLSData->nFileFinders == 0)
228     {
229         CPLFree( pTLSData->papfnFinders );
230         pTLSData->papfnFinders = nullptr;
231     }
232 
233     return pfnReturn;
234 }
235 
236 /** CPLPopFileFinder */
CPLPopFileFinder()237 CPLFileFinder CPLPopFileFinder()
238 
239 {
240     return CPLPopFileFinderInternal(CPLFinderInit());
241 }
242 
243 /************************************************************************/
244 /*                       CPLPushFinderLocation()                        */
245 /************************************************************************/
246 
247 /** CPLPushFinderLocation */
CPLPushFinderLocation(const char * pszLocation)248 void CPLPushFinderLocation( const char *pszLocation )
249 
250 {
251     FindFileTLS* pTLSData = CPLFinderInit();
252     if( pTLSData == nullptr )
253         return;
254     // Check if location already is in list.
255     if( CSLFindStringCaseSensitive(pTLSData->papszFinderLocations,
256                                    pszLocation) > -1 )
257         return;
258     pTLSData->papszFinderLocations =
259         CSLAddStringMayFail( pTLSData->papszFinderLocations, pszLocation );
260 }
261 
262 /************************************************************************/
263 /*                       CPLPopFinderLocation()                         */
264 /************************************************************************/
265 
CPLPopFinderLocationInternal(FindFileTLS * pTLSData)266 static void CPLPopFinderLocationInternal( FindFileTLS* pTLSData )
267 
268 {
269     if( pTLSData == nullptr || pTLSData->papszFinderLocations == nullptr )
270         return;
271 
272     const int nCount = CSLCount(pTLSData->papszFinderLocations);
273     if( nCount == 0 )
274         return;
275 
276     CPLFree( pTLSData->papszFinderLocations[nCount-1] );
277     pTLSData->papszFinderLocations[nCount-1] = nullptr;
278 
279     if( nCount == 1 )
280     {
281         CPLFree( pTLSData->papszFinderLocations );
282         pTLSData->papszFinderLocations = nullptr;
283     }
284 }
285 
286 /** CPLPopFinderLocation */
CPLPopFinderLocation()287 void CPLPopFinderLocation()
288 {
289     CPLPopFinderLocationInternal(CPLFinderInit());
290 }
291