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