1
/****************************************************************************
2
* PROJECT: File Interface
3
* FILE: sqFilePluginBasicPrims.c
9
* RCSID: $Id: sqFilePluginBasicPrims.c 2161 2010-04-01 19:10:46Z andreas $
11
* NOTES: See change log below.
12
* 2008-08-29 bf add stdin/stdout/stderr support
13
* 2005-03-26 IKP fix unaligned accesses to file[Size] members
14
* 2004-06-10 IKP 64-bit cleanliness
15
* 1/28/02 Tim remove non-ansi stuff
20
macro-ise use of sqFTruncate to avoid non-ansi
21
* 1/22/2002 JMM Use squeakFileOffsetType versus off_t
23
*****************************************************************************/
25
/* The basic prim code for file operations. See also the platform specific
26
* files typically named 'sq{blah}Directory.c' for details of the directory
27
* handling code. Note that the win32 platform #defines NO_STD_FILE_SUPPORT
28
* and thus bypasses this file
32
#ifndef NO_STD_FILE_SUPPORT
33
#include "FilePlugin.h"
36
The state of a file is kept in the following structure,
37
which is stored directly in a Squeak bytes object.
38
NOTE: The Squeak side is responsible for creating an
39
object with enough room to store sizeof(SQFile) bytes.
41
The session ID is used to detect stale file objects--
42
files that were still open when an image was written.
43
The file pointer of such files is meaningless.
45
Files are always opened in binary mode; Smalltalk code
46
does (or someday will do) line-end conversion if needed.
48
Writeable files are opened read/write. The stdio spec
49
requires that a positioning operation be done when
50
switching between reading and writing of a read/write
51
filestream. The lastOp field records whether the last
52
operation was a read or write operation, allowing this
53
positioning operation to be done automatically if needed.
59
squeakFileOffsetType fileSize; //JMM Nov 8th 2001 64bits we hope
60
int lastOp; // 0 = uncommitted, 1 = read, 2 = write //
78
extern struct VirtualMachine * interpreterProxy;
80
static void setFile(SQFile *f, FILE *file)
82
void *in= (void *)&file;
83
void *out= (void *)&f->file;
84
memcpy(out, in, sizeof(FILE *));
87
static void setSize(SQFile *f, squeakFileOffsetType size)
89
void *in= (void *)&size;
90
void *out= (void *)&f->fileSize;
91
memcpy(out, in, sizeof(squeakFileOffsetType));
94
static FILE *getFile(SQFile *f)
97
void *in= (void *)&f->file;
98
void *out= (void *)&file;
99
memcpy(out, in, sizeof(FILE *));
103
static squeakFileOffsetType getSize(SQFile *f)
105
squeakFileOffsetType size;
106
void *in= (void *)&f->fileSize;
107
void *out= (void *)&size;
108
memcpy(out, in, sizeof(squeakFileOffsetType));
113
sqInt sqFileAtEnd(SQFile *f) {
114
/* Return true if the file's read/write head is at the end of the file. */
116
if (!sqFileValid(f)) return interpreterProxy->success(false);
117
return ftell(getFile(f)) >= getSize(f);
120
sqInt sqFileClose(SQFile *f) {
121
/* Close the given file. */
124
if (!sqFileValid(f)) return interpreterProxy->success(false);
126
if (file != stdin && file != stdout && file != stderr)
132
f->lastOp = UNCOMMITTED;
135
sqInt sqFileDeleteNameSize(char* sqFileName, sqInt sqFileNameSize) {
136
char cFileName[1000];
139
if (sqFileNameSize >= 1000) {
140
return interpreterProxy->success(false);
143
/* copy the file name into a null-terminated C string */
144
interpreterProxy->ioFilenamefromStringofLengthresolveAliases(cFileName, sqFileName, sqFileNameSize, false);
146
err = remove(cFileName);
148
return interpreterProxy->success(false);
152
squeakFileOffsetType sqFileGetPosition(SQFile *f) {
153
/* Return the current position of the file's read/write head. */
155
squeakFileOffsetType position;
157
if (!sqFileValid(f)) return interpreterProxy->success(false);
158
position = ftell(getFile(f));
159
if (position == -1) return interpreterProxy->success(false);
163
sqInt sqFileInit(void) {
164
/* Create a session ID that is unlikely to be repeated.
165
Zero is never used for a valid session number.
166
Should be called once at startup time.
168
#if VM_PROXY_MINOR > 6
169
thisSession = (int) interpreterProxy->getThisSessionID();
171
thisSession = ioLowResMSecs() + time(NULL);
172
if (thisSession == 0) thisSession = 1; /* don't use 0 */
177
sqInt sqFileShutdown(void) {
181
static int setStdFilename(char* stdFilename, char *cFileName, char *sqFileName, sqInt sqFileNameSize)
183
if (!strncmp(stdFilename, sqFileName,sqFileNameSize)) {
184
strcpy(cFileName, stdFilename);
190
sqInt sqFileOpen(SQFile *f, char* sqFileName, sqInt sqFileNameSize, sqInt writeFlag) {
191
/* Opens the given file using the supplied sqFile structure
192
to record its state. Fails with no side effects if f is
193
already open. Files are always opened in binary mode;
194
Squeak must take care of any line-end character mapping.
197
char cFileName[1001];
199
/* don't open an already open file */
200
if (sqFileValid(f)) return interpreterProxy->success(false);
202
/* copy the file name into a null-terminated C string */
203
if (sqFileNameSize > 1000) {
204
return interpreterProxy->success(false);
207
if (setStdFilename("/dev/stdin", cFileName, sqFileName, sqFileNameSize))
209
else if (setStdFilename("/dev/stdout", cFileName, sqFileName, sqFileNameSize))
211
else if (setStdFilename("/dev/stderr", cFileName, sqFileName, sqFileNameSize))
214
interpreterProxy->ioFilenamefromStringofLengthresolveAliases(cFileName, sqFileName, sqFileNameSize, true);
217
/* First try to open an existing file read/write: */
218
if (getFile(f) == NULL)
219
setFile(f, fopen(cFileName, "r+b"));
220
if (getFile(f) == NULL) {
221
/* Previous call fails if file does not exist. In that case,
222
try opening it in write mode to create a new, empty file.
224
setFile(f, fopen(cFileName, "w+b"));
225
if (getFile(f) != NULL) {
226
char type[4],creator[4];
227
dir_GetMacFileTypeAndCreator(sqFileName, sqFileNameSize, type, creator);
228
if (strncmp(type,"BINA",4) == 0 || strncmp(type,"????",4) == 0 || *(int *)type == 0 )
229
dir_SetMacFileTypeAndCreator(sqFileName, sqFileNameSize,"TEXT","R*ch");
234
if (getFile(f) == NULL)
235
setFile(f, fopen(cFileName, "rb"));
239
if (getFile(f) == NULL) {
242
return interpreterProxy->success(false);
244
FILE *file= getFile(f);
245
f->sessionID = thisSession;
246
/* compute and cache file size */
247
fseek(file, 0, SEEK_END);
248
setSize(f, ftell(file));
249
fseek(file, 0, SEEK_SET);
251
f->lastOp = UNCOMMITTED;
254
size_t sqFileReadIntoAt(SQFile *f, size_t count, char* byteArrayIndex, size_t startIndex) {
255
/* Read count bytes from the given file into byteArray starting at
256
startIndex. byteArray is the address of the first byte of a
257
Squeak bytes object (e.g. String or ByteArray). startIndex
258
is a zero-based index; that is a startIndex of 0 starts writing
259
at the first byte of byteArray.
266
if (!sqFileValid(f)) return interpreterProxy->success(false);
268
if (f->writable && (f->lastOp == WRITE_OP)) fseek(file, 0, SEEK_CUR); /* seek between writing and reading */
269
dst = byteArrayIndex + startIndex;
270
bytesRead = fread(dst, 1, count, file);
275
sqInt sqFileRenameOldSizeNewSize(char* oldNameIndex, sqInt oldNameSize, char* newNameIndex, sqInt newNameSize) {
276
char cOldName[1000], cNewName[1000];
279
if ((oldNameSize >= 1000) || (newNameSize >= 1000)) {
280
return interpreterProxy->success(false);
283
/* copy the file names into null-terminated C strings */
284
interpreterProxy->ioFilenamefromStringofLengthresolveAliases(cOldName, oldNameIndex, oldNameSize, false);
286
interpreterProxy->ioFilenamefromStringofLengthresolveAliases(cNewName, newNameIndex, newNameSize, false);
288
err = rename(cOldName, cNewName);
290
return interpreterProxy->success(false);
294
sqInt sqFileSetPosition(SQFile *f, squeakFileOffsetType position) {
295
/* Set the file's read/write head to the given position. */
297
if (!sqFileValid(f)) return interpreterProxy->success(false);
298
fseek(getFile(f), position, SEEK_SET);
299
f->lastOp = UNCOMMITTED;
302
squeakFileOffsetType sqFileSize(SQFile *f) {
303
/* Return the length of the given file. */
305
if (!sqFileValid(f)) return interpreterProxy->success(false);
309
sqInt sqFileFlush(SQFile *f) {
310
/* Return the length of the given file. */
312
if (!sqFileValid(f)) return interpreterProxy->success(false);
317
sqInt sqFileTruncate(SQFile *f,squeakFileOffsetType offset) {
318
/* Truncate the file*/
320
if (!sqFileValid(f)) return interpreterProxy->success(false);
321
if (sqFTruncate(getFile(f), offset)) {
322
return interpreterProxy->success(false);
324
setSize(f, ftell(getFile(f)));
329
sqInt sqFileValid(SQFile *f) {
332
(getFile(f) != NULL) &&
333
(f->sessionID == thisSession));
336
size_t sqFileWriteFromAt(SQFile *f, size_t count, char* byteArrayIndex, size_t startIndex) {
337
/* Write count bytes to the given writable file starting at startIndex
338
in the given byteArray. (See comment in sqFileReadIntoAt for interpretation
339
of byteArray and startIndex).
344
squeakFileOffsetType position;
347
if (!(sqFileValid(f) && f->writable)) return interpreterProxy->success(false);
349
if (f->lastOp == READ_OP) fseek(file, 0, SEEK_CUR); /* seek between reading and writing */
350
src = byteArrayIndex + startIndex;
351
bytesWritten = fwrite(src, 1, count, file);
353
position = ftell(file);
354
if (position > getSize(f)) {
355
setSize(f, position); /* update file size */
358
if (bytesWritten != count) {
359
interpreterProxy->success(false);
361
f->lastOp = WRITE_OP;
365
sqInt sqFileThisSession() {
369
#endif /* NO_STD_FILE_SUPPORT */