1 // This file contains utilities for cross-platform file I/O.
2 
3 #include <ctype.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <stddef.h>
8 
9 #ifdef WIN32
10 	#include <Windows.h>
11 #endif
12 
13 // The Windows headers create the WIN32 symbol if we are compiling for Windows.
14 // Here, we create an analogous MACINTOSH symbol if we are compiling for Macintosh.
15 #if (defined(GENERATINGPOWERPC) || defined(GENERATING68K))
16 	#define MACINTOSH 1
17 #endif
18 
19 #include "CrossPlatformFileIO.h"
20 
21 /*	CPCreateFile(fullFilePath, overwrite, macCreator, macFileType)
22 
23 	Creates a file with the location and name specified by fullFilePath.
24 
25 	fullFilePath must be a native path.
26 
27 	If overwrite is true and a file by that name already exists, it first
28 	deletes the conflicting file. If overwrite is false and a file by that
29 	name exists, it returns an error.
30 
31 	macFileType is ignored on Windows. On Macintosh, it is used to set
32 	the new file's type. For example, use 'TEXT' for a text file.
33 
34 	macCreator is ignored on Windows. On Macintosh, it is used to set
35 	the new file's creator code. For example, use 'IGR0' (last character is zero)
36 	for an file.
37 
38 	Returns 0 if OK or an error code.
39 */
40 int
CPCreateFile(const char * fullFilePath,int overwrite,long macCreator,long macFileType)41 CPCreateFile(const char* fullFilePath, int overwrite, long macCreator, long macFileType)
42 {
43 	int err;
44 
45 	if (overwrite)							// Delete file if it exists and if overwrite is specified.
46 		CPDeleteFile(fullFilePath);			// Ignore error.
47 
48 	#ifdef MACINTOSH
49 		if (err = create(fullFilePath, 0, macCreator, macFileType))
50 			return err;
51 		return 0;
52 	#endif
53 
54 	#ifdef WIN32
55 	{
56 		HANDLE fileH;
57 		long accessMode, shareMode;
58 
59 		err = 0;
60 		accessMode = GENERIC_READ | GENERIC_WRITE;
61 		shareMode = 0;
62 		fileH = CreateFile(fullFilePath, accessMode, shareMode, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
63 		if (fileH == INVALID_HANDLE_VALUE)
64 			err = GetLastError();
65 		else
66 			CloseHandle(fileH);
67 		return err;
68 	}
69 	#endif
70 }
71 
72 /*	CPDeleteFile(fullFilePath)
73 
74 	Deletes the file specified by fullFilePath.
75 
76 	fullFilePath must be a native path.
77 
78 	Returns 0 if OK or an error code.
79 
80 	Added for Igor Pro 3.13 but works with any version. However, some error
81 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
82 	messages if you return these error codes to earlier versions of Igor.
83 */
84 int
CPDeleteFile(const char * fullFilePath)85 CPDeleteFile(const char* fullFilePath)
86 {
87 	#ifdef MACINTOSH
88 		int err;
89 
90 		if (err = fsdelete(fullFilePath, 0))
91 			return err;
92 		return 0;
93 	#endif
94 
95 	#ifdef WIN32
96 	{
97 		int err;
98 
99 		err = 0;
100 		if (DeleteFile(fullFilePath) == 0)
101 			err = GetLastError();
102 		return err;
103 	}
104 	#endif
105 }
106 
107 /*	CPOpenFile(fullFilePath, readOrWrite, fileRefPtr)
108 
109 	If readOrWrite is zero, opens an existing file for reading and returns a file reference
110 	via fileRefPtr.
111 
112 	If readOrWrite is non-zero, opens an existing file for writing or creates a new
113 	file if none exists and returns a file reference via fileRefPtr.
114 
115 	fullFilePath must be a native path.
116 
117 	Returns 0 if OK or an error code.
118 */
119 int
CPOpenFile(const char * fullFilePath,int readOrWrite,CP_FILE_REF * fileRefPtr)120 CPOpenFile(const char* fullFilePath, int readOrWrite, CP_FILE_REF* fileRefPtr)
121 {
122 	*fileRefPtr = fopen(fullFilePath, readOrWrite ? "wb" : "rb");
123 	if (*fileRefPtr == NULL)
124 		return CP_FILE_OPEN_ERROR;
125 	return 0;
126 }
127 
128 /*	CPCloseFile(fileRef)
129 
130 	Closes the referenced file.
131 
132 	Returns 0 if OK or an error code.
133 
134 	Added for Igor Pro 3.13 but works with any version. However, some error
135 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
136 	messages if you return these error codes to earlier versions of Igor.
137 */
138 int
CPCloseFile(CP_FILE_REF fileRef)139 CPCloseFile(CP_FILE_REF fileRef)
140 {
141 	if (fclose(fileRef))
142 		return CP_FILE_CLOSE_ERROR;
143 	return 0;
144 }
145 
146 /*	CPReadFile(fileRef, count, buffer, numBytesReadPtr)
147 
148 	Reads count bytes from the referenced file into the buffer.
149 
150 	If numBytesReadPtr is not NULL, stores the number of bytes read in
151 	*numBytesReadPtr.
152 
153 	Returns 0 if OK or an error code.
154 
155 	If bytes remain to be read in the file and you ask to read more bytes
156 	than remain, the remaining bytes are returned and the function result is
157 	zero. If no bytes remain to be read in the file and you ask to read bytes,
158 	no bytes are returned and the function result is CP_FILE_EOF_ERROR.
159 
160 	CPReadFile is appropriate when you are reading data of variable size, in
161 	which case you do not want to consider it an error if the end of file is reached
162 	before reading all of the bytes that you requested. If you are reading a
163 	record of fixed size, use use CPReadFile2 instead of CPReadFile.
164 
165 	Added for Igor Pro 3.13 but works with any version. However, some error
166 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
167 	messages if you return these error codes to earlier versions of Igor.
168 */
169 int
CPReadFile(CP_FILE_REF fileRef,unsigned long count,void * buffer,unsigned long * numBytesReadPtr)170 CPReadFile(CP_FILE_REF fileRef, unsigned long count, void* buffer, unsigned long* numBytesReadPtr)
171 {
172 	unsigned long numBytesRead;
173 
174 	if (count == 0) {
175 		if (numBytesReadPtr != NULL)
176 			*numBytesReadPtr = 0;
177 		return 0;
178 	}
179 
180 	clearerr(fileRef);
181 	numBytesRead = fread(buffer, 1, count, fileRef);
182 	if (numBytesReadPtr != NULL)
183 		*numBytesReadPtr = numBytesRead;
184 	if (ferror(fileRef))
185 		return CP_FILE_READ_ERROR;
186 	if (numBytesRead==0 && CPAtEndOfFile(fileRef))
187 		return CP_FILE_EOF_ERROR;			// We were at the end of file when asked to read some bytes.
188 	return 0;
189 }
190 
191 /*	CPReadFile2(fileRef, count, buffer, numBytesReadPtr)
192 
193 	Reads count bytes from the referenced file into the buffer.
194 
195 	If numBytesReadPtr is not NULL, stores the number of bytes read in
196 	*numBytesReadPtr.
197 
198 	Returns 0 if OK or an error code.
199 
200 	If bytes remain to be read in the file and you ask to read more bytes
201 	than remain, the remaining bytes are returned and the function result is
202 	CP_FILE_EOF_ERROR.
203 
204 	CPReadFile2 is appropriate when you are reading a record of fixed size, in
205 	which case you want to consider it an error if the end of file is reached
206 	before reading all of the bytes in the record. If you are reading a record
207 	of variable size then you should use CPReadFile instead of CPReadFile2.
208 
209 	Added for Igor Pro 3.13 but works with any version. However, some error
210 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
211 	messages if you return these error codes to earlier versions of Igor.
212 */
213 int
CPReadFile2(CP_FILE_REF fileRef,unsigned long count,void * buffer,unsigned long * numBytesReadPtr)214 CPReadFile2(CP_FILE_REF fileRef, unsigned long count, void* buffer, unsigned long* numBytesReadPtr)
215 {
216 	unsigned long numBytesRead;
217 
218 	if (count == 0) {
219 		if (numBytesReadPtr != NULL)
220 			*numBytesReadPtr = 0;
221 		return 0;
222 	}
223 
224 	clearerr(fileRef);
225 	numBytesRead = fread(buffer, 1, count, fileRef);
226 	if (numBytesReadPtr != NULL)
227 		*numBytesReadPtr = numBytesRead;
228 	if (ferror(fileRef))
229 		return CP_FILE_READ_ERROR;
230 	if (numBytesRead < count) {					// We did not read all of the bytes requested.
231 		if (CPAtEndOfFile(fileRef))
232 			return CP_FILE_EOF_ERROR;			// We hit the end of file.
233 		return CP_FILE_READ_ERROR;				// Some other occurred but ferror did not reflect it.
234 	}
235 	return 0;
236 }
237 
238 /*	CPWriteFile(fileRef, count, buffer, numBytesWrittenPtr)
239 
240 	Writes count bytes from the buffer to the referenced file.
241 
242 	If numBytesWrittenPtr is not NULL, stores the number of bytes written in
243 	*numBytesWrittenPtr.
244 
245 	Returns 0 if OK or an error code.
246 
247 	Added for Igor Pro 3.13 but works with any version. However, some error
248 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
249 	messages if you return these error codes to earlier versions of Igor.
250 */
251 int
CPWriteFile(CP_FILE_REF fileRef,unsigned long count,const void * buffer,unsigned long * numBytesWrittenPtr)252 CPWriteFile(CP_FILE_REF fileRef, unsigned long count, const void* buffer, unsigned long* numBytesWrittenPtr)
253 {
254 	unsigned long numBytesWritten;
255 
256 	if (count == 0) {
257 		if (numBytesWrittenPtr != NULL)
258 			*numBytesWrittenPtr = 0;
259 		return 0;
260 	}
261 
262 	numBytesWritten = fwrite(buffer, 1, count, fileRef);
263 	if (numBytesWrittenPtr != NULL)
264 		*numBytesWrittenPtr = numBytesWritten;
265 	if (numBytesWritten != count)
266 		return CP_FILE_WRITE_ERROR;
267 	return 0;
268 }
269 
270 /*	CPGetFilePosition(fileRef, filePosPtr)
271 
272 	Returns via filePosPtr the current file position of the referenced file.
273 
274 	Returns 0 if OK or an error code.
275 
276 	Added for Igor Pro 3.13 but works with any version. However, some error
277 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
278 	messages if you return these error codes to earlier versions of Igor.
279 */
280 int
CPGetFilePosition(CP_FILE_REF fileRef,unsigned long * filePosPtr)281 CPGetFilePosition(CP_FILE_REF fileRef, unsigned long* filePosPtr)
282 {
283 	long pos;
284 
285 	pos = ftell(fileRef);
286 	if (pos == -1L)
287 		return CP_FILE_POS_ERROR;
288 	*filePosPtr = pos;
289 	return 0;
290 }
291 
292 /*	CPSetFilePosition(fileRef, filePos, mode)
293 
294 	Sets the current file position in the referenced file.
295 
296 	If mode is -1, then filePos is relative to the start of the file.
297 	If mode is 0, then filePos is relative to the current file position.
298 	If mode is 1, then filePos is relative to the end of the file.
299 
300 	Returns 0 if OK or an error code.
301 
302 	Added for Igor Pro 3.13 but works with any version. However, some error
303 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
304 	messages if you return these error codes to earlier versions of Igor.
305 */
306 int
CPSetFilePosition(CP_FILE_REF fileRef,long filePos,int mode)307 CPSetFilePosition(CP_FILE_REF fileRef, long filePos, int mode)
308 {
309 	int seekMode;
310 
311 	switch(mode) {
312 		case -1:
313 			seekMode = SEEK_SET;
314 			break;
315 		case 0:
316 			seekMode = SEEK_CUR;
317 			break;
318 		case 1:
319 			seekMode = SEEK_END;
320 			break;
321 		default:
322 			return CP_FILE_POS_ERROR;
323 	}
324 
325 	if (fseek(fileRef, filePos, seekMode) != 0)
326 		return CP_FILE_POS_ERROR;
327 	return 0;
328 }
329 
330 /*	CPAtEndOfFile(fileRef)
331 
332 	Returns 1 if the current file position is at the end of file, 0 if not.
333 
334 	Added for Igor Pro 3.13 but works with any version. However, some error
335 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
336 	messages if you return these error codes to earlier versions of Igor.
337 */
338 int
CPAtEndOfFile(CP_FILE_REF fileRef)339 CPAtEndOfFile(CP_FILE_REF fileRef)
340 {
341 	if (feof(fileRef))				// Hit end of file?
342 		return 1;
343 	return 0;
344 }
345 
346 /*	CPNumberOfBytesInFile(fileRef, numBytesPtr)
347 
348 	Returns via numBytesPtr the total number of bytes in the referenced file.
349 
350 	Returns 0 if OK or an error code.
351 
352 	Added for Igor Pro 3.13 but works with any version. However, some error
353 	codes returned require Igor Pro 3.13 or later, so you will get bogus error
354 	messages if you return these error codes to earlier versions of Igor.
355 */
356 int
CPNumberOfBytesInFile(CP_FILE_REF fileRef,unsigned long * numBytesPtr)357 CPNumberOfBytesInFile(CP_FILE_REF fileRef, unsigned long* numBytesPtr)
358 {
359 	long originalPos;
360 
361 	originalPos = ftell(fileRef);
362 	if (fseek(fileRef, 0, SEEK_END) != 0)
363 		return CP_FILE_POS_ERROR;
364 	*numBytesPtr = ftell(fileRef);
365 	if (*numBytesPtr == -1L)
366 		return CP_FILE_POS_ERROR;
367 	if (fseek(fileRef, originalPos, SEEK_SET) != 0)
368 		return CP_FILE_POS_ERROR;
369 	return 0;
370 }
371