1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is mozilla.org code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* Pierre Phaneuf <pp@ludusdesign.com>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either of the GNU General Public License Version 2 or later (the "GPL"),
27
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
39
#include "nsIFileStream.h"
40
#include "nsFileSpec.h"
45
#include "nsSegmentedBuffer.h"
49
#include "pprio.h" // To get PR_ImportFile
59
//========================================================================================
61
: public nsIRandomAccessStore
62
, public nsIFileSpecOutputStream
63
, public nsIFileSpecInputStream
65
//========================================================================================
68
FileImpl(PRFileDesc* inDesc);
69
FileImpl(const nsFileSpec& inFile, int nsprMode, PRIntn accessMode);
71
// nsISupports interface
74
// nsIOpenFile interface
75
NS_IMETHOD Open(const nsFileSpec& inFile, int nsprMode, PRIntn accessMode);
77
NS_IMETHOD GetIsOpen(PRBool* outOpen);
79
// nsIInputStream interface
80
NS_IMETHOD Available(PRUint32 *aLength);
81
NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount);
82
NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval);
83
NS_IMETHOD IsNonBlocking(PRBool *aNonBlocking);
85
// nsIOutputStream interface
86
NS_IMETHOD Write(const char* aBuf, PRUint32 aCount, PRUint32 *aWriteCount);
88
NS_IMETHOD WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval);
89
NS_IMETHOD WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval);
91
// nsIRandomAccessStore interface
92
NS_DECL_NSISEEKABLESTREAM
93
NS_IMETHOD GetAtEOF(PRBool* outAtEOF);
94
NS_IMETHOD SetAtEOF(PRBool inAtEOF);
103
kOuputBufferSegmentSize = 4096,
104
kOuputBufferMaxSize = 4096
107
nsresult InternalFlush(PRBool syncFile);
108
nsresult AllocateBuffers(PRUint32 segmentSize, PRUint32 maxSize);
110
PRFileDesc* mFileDesc;
117
nsSegmentedBuffer mOutBuffer;
123
NS_IMPL_RELEASE(FileImpl)
124
NS_IMPL_ADDREF(FileImpl)
126
NS_IMPL_QUERY_HEAD(FileImpl)
127
NS_IMPL_QUERY_BODY(nsIOpenFile)
128
NS_IMPL_QUERY_BODY(nsISeekableStream)
129
NS_IMPL_QUERY_BODY(nsIRandomAccessStore)
130
NS_IMPL_QUERY_BODY(nsIOutputStream)
131
NS_IMPL_QUERY_BODY(nsIInputStream)
132
NS_IMPL_QUERY_BODY(nsIFileSpecInputStream)
133
NS_IMPL_QUERY_BODY(nsIFileSpecOutputStream)
134
NS_IMPL_QUERY_TAIL(nsIOutputStream)
137
//----------------------------------------------------------------------------------------
138
FileImpl::FileImpl(PRFileDesc* inDesc)
139
//----------------------------------------------------------------------------------------
145
, mGotBuffers(PR_FALSE)
147
mWriteCursor = nsnull;
148
mWriteLimit = nsnull;
152
//----------------------------------------------------------------------------------------
153
FileImpl::FileImpl(const nsFileSpec& inFile, int nsprMode, PRIntn accessMode)
154
//----------------------------------------------------------------------------------------
159
, mGotBuffers(PR_FALSE)
161
mWriteCursor = nsnull;
162
mWriteLimit = nsnull;
164
nsresult rv = Open(inFile, nsprMode, accessMode); // this sets nsprMode
170
char *fileName = inFile.GetLeafName();
171
printf("Opening file %s failed\n", fileName);
172
nsCRT::free(fileName);
181
//----------------------------------------------------------------------------------------
182
FileImpl::~FileImpl()
183
//----------------------------------------------------------------------------------------
185
nsresult rv = Close();
186
NS_ASSERTION(NS_SUCCEEDED(rv), "Close failed");
190
//----------------------------------------------------------------------------------------
191
NS_IMETHODIMP FileImpl::Open(
192
const nsFileSpec& inFile,
195
//----------------------------------------------------------------------------------------
198
if ((nsprMode & mNSPRMode) == nsprMode)
201
return NS_FILE_RESULT(PR_ILLEGAL_ACCESS_ERROR);
203
const int nspr_modes[]={
204
PR_WRONLY | PR_CREATE_FILE,
205
PR_WRONLY | PR_CREATE_FILE | PR_APPEND,
206
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
208
PR_RDONLY | PR_APPEND,
209
PR_RDWR | PR_CREATE_FILE,
210
PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
218
const int* currentLegalMode = nspr_modes;
219
while (*currentLegalMode && nsprMode != *currentLegalMode)
221
if (!*currentLegalMode)
222
return NS_FILE_RESULT(PR_ILLEGAL_ACCESS_ERROR);
225
// Use the file spec to open the file, because one path can be common to
226
// several files on the Macintosh (you can have several volumes with the
229
OSErr err = inFile.Error();
231
if (err != fnfErr || !(nsprMode & PR_CREATE_FILE))
232
return NS_FILE_RESULT(inFile.Error());
235
const OSType kCreator = 'CWIE';
237
const OSType kCreator = 'MOSS';
239
// Resolve the alias to the original file.
240
nsFileSpec original = inFile;
241
PRBool ignoredResult;
242
original.ResolveSymlink(ignoredResult);
243
const FSSpec& spec = original.operator const FSSpec&();
244
if (nsprMode & PR_CREATE_FILE) {
245
// In order to get the right file type/creator, do it with an nsILocalFileMac
246
// Don't propagate any errors in doing this. If any error, just use FSpCreate.
247
FSSpec nonConstSpec = spec;
248
nsCOMPtr<nsILocalFileMac> macFile;
249
nsresult res = NS_NewLocalFileWithFSSpec(&nonConstSpec, PR_FALSE, getter_AddRefs(macFile));
250
if (NS_SUCCEEDED(res)) {
251
nsCOMPtr<nsIFile> asFile(do_QueryInterface(macFile, &res));
252
if (NS_SUCCEEDED(res)) {
253
res = asFile->Create(nsIFile::NORMAL_FILE_TYPE, 0);
254
if (res == NS_ERROR_FILE_ALREADY_EXISTS)
259
err = FSpCreate(&spec, kCreator, 'TEXT', 0);
265
return NS_FILE_RESULT(err);
268
if (nsprMode & PR_RDWR)
270
else if (nsprMode & PR_WRONLY)
276
err = FSpOpenDF(&spec, perm, &refnum);
278
if (err == noErr && (nsprMode & PR_TRUNCATE))
279
err = ::SetEOF(refnum, 0);
280
if (err == noErr && (nsprMode & PR_APPEND))
281
err = SetFPos(refnum, fsFromLEOF, 0);
283
return NS_FILE_RESULT(err);
285
if ((mFileDesc = PR_ImportFile(refnum)) == 0)
286
return NS_FILE_RESULT(PR_GetError());
288
// Platforms other than Macintosh...
289
// Another bug in NSPR: Mac PR_Open assumes a unix style path, but Win PR_Open assumes
291
if ((mFileDesc = PR_Open((const char*)nsFileSpec(inFile), nsprMode, accessMode)) == 0)
292
return NS_FILE_RESULT(PR_GetError());
294
mNSPRMode = nsprMode;
295
mLength = PR_Available(mFileDesc);
300
//----------------------------------------------------------------------------------------
301
NS_IMETHODIMP FileImpl::Available(PRUint32 *aLength)
302
//----------------------------------------------------------------------------------------
304
NS_PRECONDITION(aLength != nsnull, "null ptr");
306
return NS_ERROR_NULL_POINTER;
308
return NS_ERROR_UNEXPECTED;
313
//----------------------------------------------------------------------------------------
314
NS_IMETHODIMP FileImpl::GetIsOpen(PRBool* outOpen)
315
//----------------------------------------------------------------------------------------
317
*outOpen = (mFileDesc != nsnull && !mFailed);
321
//----------------------------------------------------------------------------------------
322
NS_IMETHODIMP FileImpl::Seek(PRInt32 whence, PRInt64 offset)
323
//----------------------------------------------------------------------------------------
325
if (mFileDesc==PR_STDIN || mFileDesc==PR_STDOUT || mFileDesc==PR_STDERR || !mFileDesc)
326
return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR);
327
mFailed = PR_FALSE; // reset on a seek.
328
mEOF = PR_FALSE; // reset on a seek.
330
// To avoid corruption, we flush during a seek. see bug number 18949
331
InternalFlush(PR_FALSE);
333
nsInt64 position = PR_Seek64(mFileDesc, 0, PR_SEEK_CUR);
334
nsInt64 available = PR_Available64(mFileDesc);
335
nsInt64 fileSize = position + available;
336
nsInt64 newPosition = offset;
339
case NS_SEEK_CUR: newPosition += position; break;
340
case NS_SEEK_SET: ; break;
341
case NS_SEEK_END: newPosition += fileSize; break;
343
const nsInt64 zero = 0;
344
if (newPosition < zero)
349
if (newPosition >= fileSize) // nb: not "else if".
351
newPosition = fileSize;
354
if (PR_Seek64(mFileDesc, newPosition, PR_SEEK_SET) < 0)
360
//----------------------------------------------------------------------------------------
361
NS_IMETHODIMP FileImpl::Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount)
362
//----------------------------------------------------------------------------------------
364
NS_PRECONDITION(aBuf != nsnull, "null ptr");
366
return NS_ERROR_NULL_POINTER;
367
NS_PRECONDITION(aReadCount != nsnull, "null ptr");
369
return NS_ERROR_NULL_POINTER;
371
return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR);
373
return NS_ERROR_FAILURE;
374
PRInt32 bytesRead = PR_Read(mFileDesc, aBuf, aCount);
379
return NS_FILE_RESULT(PR_GetError());
381
else if (bytesRead == 0)
385
*aReadCount = bytesRead;
390
FileImpl::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
392
NS_NOTREACHED("ReadSegments");
393
return NS_ERROR_NOT_IMPLEMENTED;
396
//----------------------------------------------------------------------------------------
397
NS_IMETHODIMP FileImpl::Write(const char* aBuf, PRUint32 aCount, PRUint32 *aWriteCount)
398
//----------------------------------------------------------------------------------------
400
NS_PRECONDITION(aBuf != nsnull, "null ptr");
401
NS_PRECONDITION(aWriteCount != nsnull, "null ptr");
406
// Calling PR_Write on stdout is sure suicide.
407
if (mFileDesc == PR_STDOUT || mFileDesc == PR_STDERR)
409
std::cout.write(aBuf, aCount);
410
*aWriteCount = aCount;
415
return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR);
417
return NS_ERROR_FAILURE;
421
nsresult rv = AllocateBuffers(kOuputBufferSegmentSize, kOuputBufferMaxSize);
423
return rv; // try to write non-buffered?
426
PRUint32 bufOffset = 0;
427
PRUint32 currentWrite = 0;
430
if (mWriteCursor == nsnull || mWriteCursor == mWriteLimit)
432
char* seg = mOutBuffer.AppendNewSegment();
435
// buffer is full, try again
436
InternalFlush(PR_FALSE);
437
seg = mOutBuffer.AppendNewSegment();
439
return NS_ERROR_OUT_OF_MEMORY;
442
mWriteLimit = seg + mOutBuffer.GetSegmentSize();
446
currentWrite = mWriteLimit - mWriteCursor;
448
if (aCount < currentWrite)
449
currentWrite = aCount;
451
memcpy(mWriteCursor, (aBuf + bufOffset), currentWrite);
453
mWriteCursor += currentWrite;
455
aCount -= currentWrite;
456
bufOffset += currentWrite;
457
*aWriteCount += currentWrite;
464
nsWriteSegmentToFile(nsIInputStream* in,
466
const char* fromRawSegment,
469
PRUint32 *writeCount)
471
NS_NOTREACHED("nsWriteSegmentToFile");
472
return NS_ERROR_NOT_IMPLEMENTED;
476
FileImpl::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *result)
478
return inStr->ReadSegments(nsWriteSegmentToFile, nsnull, count, result);
482
FileImpl::WriteSegments(nsReadSegmentFun reader, void * closure,
483
PRUint32 count, PRUint32 *result)
485
NS_NOTREACHED("WriteSegments");
486
return NS_ERROR_NOT_IMPLEMENTED;
490
FileImpl::IsNonBlocking(PRBool *aNonBlocking)
492
*aNonBlocking = PR_FALSE;
496
//----------------------------------------------------------------------------------------
497
NS_IMETHODIMP FileImpl::Tell(PRInt64* outWhere)
498
//----------------------------------------------------------------------------------------
500
if (mFileDesc==PR_STDIN || mFileDesc==PR_STDOUT || mFileDesc==PR_STDERR || !mFileDesc)
501
return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR);
502
*outWhere = PR_Seek64(mFileDesc, 0, PR_SEEK_CUR);
506
//----------------------------------------------------------------------------------------
507
NS_IMETHODIMP FileImpl::Close()
508
//----------------------------------------------------------------------------------------
510
if ((mNSPRMode & PR_RDONLY) == 0)
511
InternalFlush(PR_FALSE);
513
if (mFileDesc==PR_STDIN || mFileDesc==PR_STDOUT || mFileDesc==PR_STDERR || !mFileDesc)
515
if (PR_Close(mFileDesc) == PR_SUCCESS)
518
return NS_FILE_RESULT(PR_GetError());
522
//----------------------------------------------------------------------------------------
523
NS_IMETHODIMP FileImpl::Flush()
524
//----------------------------------------------------------------------------------------
526
// for external callers, this will do a Sync as well as flush buffers.
527
return InternalFlush(PR_TRUE);
531
//----------------------------------------------------------------------------------------
532
NS_IMETHODIMP FileImpl::GetAtEOF(PRBool* outAtEOF)
533
//----------------------------------------------------------------------------------------
540
//----------------------------------------------------------------------------------------
541
NS_IMETHODIMP FileImpl::SetAtEOF(PRBool inAtEOF)
542
//----------------------------------------------------------------------------------------
548
//----------------------------------------------------------------------------------------
549
NS_IMETHODIMP FileImpl::SetEOF()
550
//----------------------------------------------------------------------------------------
552
NS_NOTYETIMPLEMENTED("FileImpl::SetEOF");
553
return NS_ERROR_NOT_IMPLEMENTED;
556
//----------------------------------------------------------------------------------------
557
nsresult FileImpl::AllocateBuffers(PRUint32 segmentSize, PRUint32 maxBufSize)
558
//----------------------------------------------------------------------------------------
560
nsresult rv = mOutBuffer.Init(segmentSize, maxBufSize);
561
if (NS_SUCCEEDED(rv))
562
mGotBuffers = PR_TRUE;
567
// external callers of Flush will have sync get called,
568
// but internal callers just want to flush the buffers to disk.
569
nsresult FileImpl::InternalFlush(PRBool syncFile)
572
if (mFileDesc == PR_STDOUT || mFileDesc == PR_STDERR)
579
return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR);
581
PRInt32 segCount = mOutBuffer.GetSegmentCount();
582
PRUint32 segSize = mOutBuffer.GetSegmentSize();
584
for (PRInt32 i = 0; i < segCount; i++)
586
char* seg = mOutBuffer.GetSegment(i);
588
// if it is the last buffer, it may not be completely full.
589
if(i == (segCount-1))
590
segSize = (mWriteCursor - seg);
592
PRInt32 bytesWrit = PR_Write(mFileDesc, seg, segSize);
593
if (bytesWrit != (PRInt32)segSize)
596
return NS_FILE_RESULT(PR_GetError());
602
mWriteCursor = nsnull;
603
mWriteLimit = nsnull;
605
// On unix, it seems to fail always.
606
if (syncFile && PR_Sync(mFileDesc) != PR_SUCCESS)
611
//----------------------------------------------------------------------------------------
612
nsresult NS_NewTypicalInputFileStream(
613
nsISupports** aResult,
614
const nsFileSpec& inFile
615
/*Default nsprMode == PR_RDONLY*/
616
/*Default accessmode = 0666 (octal)*/)
617
// Factory method to get an nsInputStream from a file, using most common options
618
//----------------------------------------------------------------------------------------
620
// This QueryInterface was needed because NS_NewIOFileStream
621
// does a cast from (void *) to (nsISupports *) thus causing a
622
// vtable problem on Windows, where we really didn't have the proper pointer
623
// to an nsIInputStream, this ensures that we do
624
nsISupports * supports;
625
nsIInputStream * inStr;
627
nsresult rv = NS_NewIOFileStream(&supports, inFile, PR_RDONLY, 0666);
630
if (NS_SUCCEEDED(rv)) {
631
if (NS_SUCCEEDED(supports->QueryInterface(NS_GET_IID(nsIInputStream), (void**)&inStr))) {
634
NS_RELEASE(supports);
639
//----------------------------------------------------------------------------------------
640
nsresult NS_NewTypicalOutputFileStream(
641
nsISupports** aResult,
642
const nsFileSpec& inFile
643
/*default nsprMode= (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE)*/
644
/*Default accessMode= 0666 (octal)*/)
645
// Factory method to get an nsOutputStream to a file - most common case.
646
//----------------------------------------------------------------------------------------
648
// This QueryInterface was needed because NS_NewIOFileStream
649
// does a cast from (void *) to (nsISupports *) thus causing a
650
// vtable problem on Windows, where we really didn't have the proper pointer
651
// to an nsIOutputStream, this ensures that we do
653
/* nsISupports * supports;
654
nsIOutputStream * outStr;
656
nsresult rv = NS_NewIOFileStream(
659
(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
663
if (NS_SUCCEEDED(rv)) {
664
if (NS_SUCCEEDED(supports->QueryInterface(NS_GET_IID(nsIOutputStream), (void**)&outStr))) {
667
NS_RELEASE(supports);
672
nsCOMPtr<nsISupports> supports;
673
nsIOutputStream * outStr;
675
nsresult rv = NS_NewIOFileStream(
676
getter_AddRefs(supports),
678
(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
682
if (NS_SUCCEEDED(rv)) {
683
if (NS_SUCCEEDED(supports->QueryInterface(NS_GET_IID(nsIOutputStream), (void**)&outStr))) {
689
return NS_NewIOFileStream(
692
(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
697
//----------------------------------------------------------------------------------------
698
NS_COM_OBSOLETE nsresult NS_NewIOFileStream(
699
nsISupports** aResult,
700
const nsFileSpec& inFile,
701
PRInt32 nsprMode /*default = (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE)*/,
702
PRInt32 accessMode /*Default = 0666 (octal)*/)
703
// Factory method to get an object that implements both nsIInputStream
704
// and nsIOutputStream, associated with a file.
705
//----------------------------------------------------------------------------------------
707
NS_PRECONDITION(aResult != nsnull, "null ptr");
709
return NS_ERROR_NULL_POINTER;
711
FileImpl* stream = new FileImpl(inFile, nsprMode, accessMode);
713
return NS_ERROR_OUT_OF_MEMORY;
716
PRBool isOpened = PR_FALSE;
717
stream->GetIsOpen(&isOpened);
721
return NS_ERROR_FAILURE;
724
*aResult = (nsISupports*)(void*)stream;