1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2010 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9
10 #include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
11
12 #include "public/include/XMP_Const.h"
13 #include "public/include/XMP_IO.hpp"
14
15 #include "source/XIO.hpp"
16 #include "source/XMP_LibUtils.hpp"
17 #include "source/UnicodeConversions.hpp"
18
19 #if XMP_WinBuild
20 #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
21 #endif
22
23 // =================================================================================================
24
MakeLowerCase(std::string * str)25 static inline void MakeLowerCase ( std::string * str )
26 {
27 for ( size_t i = 0, limit = str->size(); i < limit; ++i ) {
28 char ch = (*str)[i];
29 if ( ('A' <= ch) && (ch <= 'Z') ) (*str)[i] += 0x20;
30 }
31 }
32
33 // =================================================================================================
34 // XIO::SplitLeafName
35 // ==================
36
SplitLeafName(std::string * path,std::string * leafName)37 void XIO::SplitLeafName ( std::string * path, std::string * leafName )
38 {
39 size_t dirPos = path->size();
40 // Return if path is empty or just the slash or DOT
41 if ( dirPos == 0 || (dirPos == 1 &&
42 ((*path)[dirPos-1] == kDirChar
43 || (*path)[dirPos-1] == '.')
44 )
45 )
46 {
47 leafName->erase();
48 path->erase();
49 return;
50 }
51
52 // Remove trailing slashes
53 --dirPos;
54 #if XMP_WinBuild
55 if ( (*path)[dirPos] == '/' ) (*path)[dirPos] = kDirChar; // Tolerate both '\' and '/'.
56 #endif
57 if ( (*path)[dirPos] == kDirChar )
58 {
59 path->erase(dirPos);
60 }
61
62 // Search next slash
63 for ( --dirPos; dirPos > 0; --dirPos ) {
64 #if XMP_WinBuild
65 if ( (*path)[dirPos] == '/' ) (*path)[dirPos] = kDirChar; // Tolerate both '\' and '/'.
66 #endif
67 if ( (*path)[dirPos] == kDirChar ) break;
68 }
69
70 if ( (*path)[dirPos] == kDirChar ) {
71 leafName->assign ( &(*path)[dirPos+1] );
72 path->erase ( dirPos );
73 } else if ( dirPos == 0 ) {
74 leafName->erase();
75 leafName->swap ( *path );
76 }
77
78 } // XIO::SplitLeafName
79
80 // =================================================================================================
81 // XIO::SplitFileExtension
82 // =======================
83 //
84 // ! Must only be called after using SplitLeafName!
85
SplitFileExtension(std::string * leafName,std::string * fileExt,bool lowercase)86 void XIO::SplitFileExtension ( std::string * leafName, std::string * fileExt , bool lowercase )
87 {
88
89 fileExt->erase();
90
91 size_t extPos = leafName->size();
92 if ( extPos == 0 ) return;
93
94 for ( --extPos; extPos > 0; --extPos ) if ( (*leafName)[extPos] == '.' ) break;
95
96 if ( (*leafName)[extPos] == '.' ) {
97 fileExt->assign ( &((*leafName)[extPos+1]) );
98 if (lowercase) MakeLowerCase ( fileExt );
99 leafName->erase ( extPos );
100 }
101
102 } // XIO::SplitFileExtension
103
104 // =================================================================================================
105 // XIO::ReplaceTextFile
106 // ====================
107 //
108 // Replace the contents of a text file in a manner that preserves the meaning of the old content if
109 // a disk full error occurs. This can mean appended spaces appear in the old content.
110
ReplaceTextFile(XMP_IO * textFile,const std::string & newContent,bool doSafeUpdate)111 void XIO::ReplaceTextFile ( XMP_IO* textFile, const std::string & newContent, bool doSafeUpdate )
112 {
113 XMP_Int64 newContentSize = (XMP_Int64)newContent.size();
114 XMP_Enforce ( newContentSize <= (XMP_Int64)0xFFFFFFFFULL ); // Make sure it fits in UInt32 for Write.
115
116 if ( doSafeUpdate ) {
117
118 // Safe updates are no problem, the old content is untouched if the temp file write fails.
119
120 XMP_IO* tempFile = textFile->DeriveTemp();
121 tempFile->Write ( newContent.data(), (XMP_Uns32)newContentSize );
122 textFile->AbsorbTemp();
123
124 } else {
125
126 // We're overwriting the existing file. Make sure it is big enough, write the new content,
127 // then truncate if necessary.
128
129 XMP_Int64 oldContentSize = textFile->Length();
130
131 if ( oldContentSize < newContentSize ) {
132 size_t spaceCount = (size_t) (newContentSize - oldContentSize); // AUDIT: newContentSize fits in UInt32.
133 std::string spaces;
134 spaces.assign ( spaceCount, ' ' );
135 textFile->ToEOF();
136 textFile->Write ( spaces.data(), (XMP_Uns32)spaceCount );
137 }
138
139 XMP_Assert ( newContentSize <= textFile->Length() );
140 textFile->Rewind();
141 textFile->Write ( newContent.data(), (XMP_Uns32)newContentSize );
142
143 if ( oldContentSize > newContentSize ) textFile->Truncate ( newContentSize );
144
145 }
146
147 } // XIO::ReplaceTextFile
148
149 // =================================================================================================
150 // XIO::Copy
151 // =========
152
Copy(XMP_IO * sourceFile,XMP_IO * destFile,XMP_Int64 length,XMP_AbortProc abortProc,void * abortArg)153 void XIO::Copy ( XMP_IO* sourceFile, XMP_IO* destFile, XMP_Int64 length,
154 XMP_AbortProc abortProc /* = 0 */, void* abortArg /* = 0 */ )
155 {
156 const bool checkAbort = (abortProc != 0);
157 XMP_Uns8 buffer [64*1024];
158
159 while ( length > 0 ) {
160
161 if ( checkAbort && abortProc(abortArg) ) {
162 XMP_Throw ( "XIO::Copy, user abort", kXMPErr_UserAbort );
163 }
164
165 XMP_Int32 ioCount = sizeof(buffer);
166 if ( length < ioCount ) ioCount = (XMP_Int32)length;
167
168 sourceFile->Read ( buffer, ioCount, XMP_IO::kReadAll );
169 destFile->Write ( buffer, ioCount );
170 length -= ioCount;
171
172 }
173
174 } // XIO::Copy
175
176 // =================================================================================================
177 // XIO::Move
178 // =========
179
180 // allows to move data within a file (just pass in the same file handle as srcFile and dstFile)
181 // shadow effects (stumbling over just-written data) are avoided.
182 //
183 // * however can also be used to move data between two files *
184 // (having both option is handy for flexible use in update()/re-write() handler routines)
185
Move(XMP_IO * srcFile,XMP_Int64 srcOffset,XMP_IO * dstFile,XMP_Int64 dstOffset,XMP_Int64 length,XMP_AbortProc abortProc,void * abortArg)186 void XIO::Move ( XMP_IO* srcFile, XMP_Int64 srcOffset,
187 XMP_IO* dstFile, XMP_Int64 dstOffset,
188 XMP_Int64 length, XMP_AbortProc abortProc /* = 0 */, void * abortArg /* = 0 */ )
189 {
190 enum { kBufferLen = 64*1024 };
191 XMP_Uns8 buffer [kBufferLen];
192
193 const bool checkAbort = (abortProc != 0);
194
195 if ( srcOffset > dstOffset ) { // avoiding shadow effects
196
197 // move down -> shift lowest packet first !
198
199 while ( length > 0 ) {
200
201 if ( checkAbort && abortProc(abortArg) ) XMP_Throw ( "XIO::Move - User abort", kXMPErr_UserAbort );
202 XMP_Int32 ioCount = kBufferLen;
203 if ( length < kBufferLen ) ioCount = (XMP_Int32)length; //smartly avoids 32/64 bit issues
204
205 srcFile->Seek ( srcOffset, kXMP_SeekFromStart );
206 srcFile->ReadAll ( buffer, ioCount );
207 dstFile->Seek ( dstOffset, kXMP_SeekFromStart );
208 dstFile->Write ( buffer, ioCount );
209 length -= ioCount;
210
211 srcOffset += ioCount;
212 dstOffset += ioCount;
213
214 }
215
216 } else { // move up -> shift highest packet first
217
218 srcOffset += length; //move to end
219 dstOffset += length;
220
221 while ( length > 0 ) {
222
223 if ( checkAbort && abortProc(abortArg) ) XMP_Throw ( "XIO::Move - User abort", kXMPErr_UserAbort );
224 XMP_Int32 ioCount = kBufferLen;
225 if ( length < kBufferLen ) ioCount = (XMP_Int32)length; //smartly avoids 32/64 bit issues
226
227 srcOffset -= ioCount;
228 dstOffset -= ioCount;
229
230 srcFile->Seek ( srcOffset, kXMP_SeekFromStart );
231 srcFile->ReadAll ( buffer, ioCount );
232 dstFile->Seek ( dstOffset, kXMP_SeekFromStart );
233 dstFile->Write ( buffer, ioCount );
234 length -= ioCount;
235
236 }
237
238 }
239
240 } // XIO::Move
241
242 // =================================================================================================
243