1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
25 #include <unotools/tempfile.hxx>
26 #include <rtl/ustring.hxx>
27 #include <rtl/instance.hxx>
28 #include <osl/detail/file.h>
29 #include <osl/file.hxx>
30 #include <tools/time.hxx>
31 #include <tools/debug.hxx>
43 struct TempNameBase_Impl
44 : public rtl::Static
< OUString
, TempNameBase_Impl
> {};
50 static OUString
getParentName( const OUString
& aFileName
)
52 sal_Int32 lastIndex
= aFileName
.lastIndexOf( '/' );
57 aParent
= aFileName
.copy(0, lastIndex
);
59 if (aParent
.endsWith(":") && aParent
.getLength() == 6)
62 if (aParent
.equalsIgnoreAsciiCase("file://"))
69 static bool ensuredir( const OUString
& rUnqPath
)
72 if ( rUnqPath
.isEmpty() )
75 // remove trailing slash
76 if ( rUnqPath
.endsWith("/") )
77 aPath
= rUnqPath
.copy( 0, rUnqPath
.getLength() - 1 );
81 // HACK: create directory on a mount point with nobrowse option
82 // returns ENOSYS in any case !!
83 osl::Directory
aDirectory( aPath
);
84 osl::FileBase::RC nError
= aDirectory
.open();
86 if( nError
== osl::File::E_None
)
89 // try to create the directory
90 nError
= osl::Directory::create( aPath
);
91 bool bSuccess
= ( nError
== osl::File::E_None
|| nError
== osl::FileBase::E_EXIST
);
94 // perhaps parent(s) don't exist
95 OUString aParentDir
= getParentName( aPath
);
96 if ( aParentDir
!= aPath
)
98 bSuccess
= ensuredir( getParentName( aPath
) );
100 // After parent directory structure exists try it one's more
103 // Parent directory exists, retry creation of directory
104 nError
= osl::Directory::create( aPath
);
105 bSuccess
=( nError
== osl::File::E_None
|| nError
== osl::FileBase::E_EXIST
);
113 static OUString
ConstructTempDir_Impl( const OUString
* pParent
)
117 // Ignore pParent on iOS. We don't want to create any temp files
118 // in the same directory where the document being edited is.
120 if ( pParent
&& !pParent
->isEmpty() )
122 // test for valid filename
124 if ((osl::FileBase::getSystemPathFromFileURL(*pParent
, aRet
)
125 == osl::FileBase::E_None
)
126 && (osl::FileBase::getFileURLFromSystemPath(aRet
, aRet
)
127 == osl::FileBase::E_None
))
129 ::osl::DirectoryItem aItem
;
130 sal_Int32 i
= aRet
.getLength();
131 if ( aRet
[i
-1] == '/' )
134 if ( DirectoryItem::get( aRet
.copy(0, i
), aItem
) == FileBase::E_None
)
142 if ( aName
.isEmpty() )
144 OUString
&rTempNameBase_Impl
= TempNameBase_Impl::get();
145 if (rTempNameBase_Impl
.isEmpty())
147 OUString ustrTempDirURL
;
148 ::osl::FileBase::RC rc
= ::osl::File::getTempDirURL(
150 if (rc
== ::osl::FileBase::E_None
)
151 rTempNameBase_Impl
= ustrTempDirURL
;
153 // if no parent or invalid parent : use default directory
154 DBG_ASSERT( !rTempNameBase_Impl
.isEmpty(), "No TempDir!" );
155 aName
= rTempNameBase_Impl
;
159 // Make sure that directory ends with a separator
160 if( !aName
.isEmpty() && !aName
.endsWith("/") )
168 virtual bool next(OUString
*) = 0;
171 virtual ~Tokens() {} // avoid warnings
174 class SequentialTokens
: public Tokens
{
176 explicit SequentialTokens(bool showZero
): m_value(0), m_show(showZero
) {}
178 bool next(OUString
* token
) override
{
179 assert(token
!= nullptr);
180 if (m_value
== SAL_MAX_UINT32
) {
183 *token
= m_show
? OUString::number(m_value
) : OUString();
194 class UniqueTokens
: public Tokens
{
196 UniqueTokens(): m_count(0) {}
198 bool next(OUString
* token
) override
{
199 assert(token
!= nullptr);
200 // Because of the shared globalValue, no single instance of UniqueTokens
201 // is guaranteed to exhaustively test all 36^6 possible values, but stop
202 // after that many attempts anyway:
203 sal_uInt32 radix
= 36;
204 sal_uInt32 max
= radix
* radix
* radix
* radix
* radix
* radix
;
205 // 36^6 == 2'176'782'336 < SAL_MAX_UINT32 == 4'294'967'295
206 if (m_count
== max
) {
211 osl::MutexGuard
g(osl::Mutex::getGlobalMutex());
213 = ((globalValue
== SAL_MAX_UINT32
214 ? tools::Time::GetSystemTicks() : globalValue
+ 1)
218 *token
= OUString::number(v
, radix
);
224 static sal_uInt32 globalValue
;
229 sal_uInt32
UniqueTokens::globalValue
= SAL_MAX_UINT32
;
233 class TempDirCreatedObserver
: public DirectoryCreationObserver
236 virtual void DirectoryCreated(const OUString
& aDirectoryUrl
) override
238 File::setAttributes( aDirectoryUrl
, osl_File_Attribute_OwnRead
|
239 osl_File_Attribute_OwnWrite
| osl_File_Attribute_OwnExe
);
244 static OUString
lcl_createName(
245 const OUString
& rLeadingChars
, Tokens
& tokens
, const OUString
* pExtension
,
246 const OUString
* pParent
, bool bDirectory
, bool bKeep
, bool bLock
,
247 bool bCreateParentDirs
)
249 OUString aName
= ConstructTempDir_Impl( pParent
);
250 if ( bCreateParentDirs
)
252 sal_Int32 nOffset
= rLeadingChars
.lastIndexOf("/");
255 OUString aDirName
= aName
+ rLeadingChars
.copy( 0, nOffset
);
256 TempDirCreatedObserver observer
;
257 FileBase::RC err
= Directory::createPath( aDirName
, &observer
);
258 if ( err
!= FileBase::E_None
&& err
!= FileBase::E_EXIST
)
262 aName
+= rLeadingChars
;
265 while (tokens
.next(&token
))
267 OUString
aTmp( aName
+ token
);
274 FileBase::RC err
= Directory::create(
276 (osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
277 | osl_File_OpenFlag_Private
));
278 if ( err
== FileBase::E_None
)
280 // !bKeep: only for creating a name, not a file or directory
281 if ( bKeep
|| Directory::remove( aTmp
) == FileBase::E_None
)
286 else if ( err
!= FileBase::E_EXIST
)
287 // if f.e. name contains invalid chars stop trying to create dirs
292 DBG_ASSERT( bKeep
, "Too expensive, use directory for creating name!" );
294 FileBase::RC err
= aFile
.open(
295 osl_File_OpenFlag_Create
| osl_File_OpenFlag_Private
296 | (bLock
? 0 : osl_File_OpenFlag_NoLock
));
297 if ( err
== FileBase::E_None
|| (bLock
&& err
== FileBase::E_NOLCK
) )
302 else if ( err
!= FileBase::E_EXIST
)
304 // if f.e. name contains invalid chars stop trying to create dirs
305 // but if there is a folder with such name proceed further
307 DirectoryItem aTmpItem
;
308 FileStatus
aTmpStatus( osl_FileStatus_Mask_Type
);
309 if ( DirectoryItem::get( aTmp
, aTmpItem
) != FileBase::E_None
310 || aTmpItem
.getFileStatus( aTmpStatus
) != FileBase::E_None
311 || aTmpStatus
.getFileType() != FileStatus::Directory
)
319 static OUString
CreateTempName_Impl( const OUString
* pParent
, bool bKeep
, bool bDir
= true )
321 OUString aEyeCatcher
= "lu";
324 const char* eye
= getenv("LO_TESTNAME");
327 aEyeCatcher
= OUString(eye
, strlen(eye
), RTL_TEXTENCODING_ASCII_US
);
330 static const pid_t pid
= getpid();
331 static const OUString aPidString
= OUString::number(pid
);
332 aEyeCatcher
+= aPidString
;
335 static const int pid
= _getpid();
336 static const OUString aPidString
= OUString::number(pid
);
337 aEyeCatcher
+= aPidString
;
340 return lcl_createName( aEyeCatcher
, t
, nullptr, pParent
, bDir
, bKeep
,
344 OUString
TempFile::CreateTempName()
346 OUString
aName(CreateTempName_Impl( nullptr, false ));
348 // convert to file URL
350 if ( !aName
.isEmpty() )
351 FileBase::getSystemPathFromFileURL( aName
, aTmp
);
355 TempFile::TempFile( const OUString
* pParent
, bool bDirectory
)
356 : bIsDirectory( bDirectory
)
357 , bKillingFileEnabled( false )
359 aName
= CreateTempName_Impl( pParent
, true, bDirectory
);
362 TempFile::TempFile( const OUString
& rLeadingChars
, bool _bStartWithZero
,
363 const OUString
* pExtension
, const OUString
* pParent
,
364 bool bCreateParentDirs
)
365 : bIsDirectory( false )
366 , bKillingFileEnabled( false )
368 SequentialTokens
t(_bStartWithZero
);
369 aName
= lcl_createName( rLeadingChars
, t
, pExtension
, pParent
, false,
370 true, true, bCreateParentDirs
);
373 TempFile::TempFile(TempFile
&& other
) noexcept
:
374 aName(std::move(other
.aName
)), pStream(std::move(other
.pStream
)), bIsDirectory(other
.bIsDirectory
),
375 bKillingFileEnabled(other
.bKillingFileEnabled
)
377 other
.bKillingFileEnabled
= false;
380 TempFile::~TempFile()
383 if ( bKillingFileEnabled
)
387 // at the moment no recursiv algorithm present
388 Directory::remove( aName
);
392 File::remove( aName
);
397 bool TempFile::IsValid() const
399 return !aName
.isEmpty();
402 OUString
TempFile::GetFileName() const
405 FileBase::getSystemPathFromFileURL(aName
, aTmp
);
409 OUString
const & TempFile::GetURL() const
414 SvStream
* TempFile::GetStream( StreamMode eMode
)
418 if (!aName
.isEmpty())
419 pStream
.reset(new SvFileStream(aName
, eMode
));
421 pStream
.reset(new SvMemoryStream(nullptr, 0, eMode
));
424 return pStream
.get();
427 void TempFile::CloseStream()
432 OUString
TempFile::SetTempNameBaseDirectory( const OUString
&rBaseName
)
434 if( rBaseName
.isEmpty() )
437 OUString
aUnqPath( rBaseName
);
439 // remove trailing slash
440 if ( rBaseName
.endsWith("/") )
441 aUnqPath
= rBaseName
.copy( 0, rBaseName
.getLength() - 1 );
443 // try to create the directory
445 osl::FileBase::RC err
= osl::Directory::create( aUnqPath
);
446 if ( err
!= FileBase::E_None
&& err
!= FileBase::E_EXIST
)
447 // perhaps parent(s) don't exist
448 bRet
= ensuredir( aUnqPath
);
452 // failure to create base directory means returning an empty string
456 // append own internal directory
457 OUString
&rTempNameBase_Impl
= TempNameBase_Impl::get();
458 rTempNameBase_Impl
= rBaseName
+ "/";
460 TempFile
aBase( nullptr, true );
461 if ( aBase
.IsValid() )
462 // use it in case of success
463 rTempNameBase_Impl
= aBase
.aName
;
465 // return system path of used directory
466 FileBase::getSystemPathFromFileURL( rTempNameBase_Impl
, aTmp
);
472 OUString
TempFile::GetTempNameBaseDirectory()
474 return ConstructTempDir_Impl(nullptr);
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */