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 const nsInt64 zero
= 0;
334 nsInt64 position
= PR_Seek64(mFileDesc
, 0, PR_SEEK_CUR
);
335 nsInt64 available
= PR_Available64(mFileDesc
);
336 if (position
< zero
|| available
< zero
)
337 return NS_FILE_RESULT(PR_FILE_SEEK_ERROR
);
338 nsInt64 fileSize
= position
+ available
;
339 nsInt64 newPosition
= offset
;
342 case NS_SEEK_CUR
: newPosition
+= position
; break;
343 case NS_SEEK_SET
: ; break;
344 case NS_SEEK_END
: newPosition
+= fileSize
; break;
346 if (newPosition
< zero
)
351 if (newPosition
>= fileSize
) // nb: not "else if".
353 newPosition
= fileSize
;
356 if (PR_Seek64(mFileDesc
, newPosition
, PR_SEEK_SET
) < 0)
362 //----------------------------------------------------------------------------------------
363 NS_IMETHODIMP
FileImpl::Read(char* aBuf
, PRUint32 aCount
, PRUint32
*aReadCount
)
364 //----------------------------------------------------------------------------------------
366 NS_PRECONDITION(aBuf
!= nsnull
, "null ptr");
368 return NS_ERROR_NULL_POINTER
;
369 NS_PRECONDITION(aReadCount
!= nsnull
, "null ptr");
371 return NS_ERROR_NULL_POINTER
;
373 return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR
);
375 return NS_ERROR_FAILURE
;
376 PRInt32 bytesRead
= PR_Read(mFileDesc
, aBuf
, aCount
);
381 return NS_FILE_RESULT(PR_GetError());
383 else if (bytesRead
== 0)
387 *aReadCount
= bytesRead
;
392 FileImpl::ReadSegments(nsWriteSegmentFun writer
, void * closure
, PRUint32 count
, PRUint32
*_retval
)
394 NS_NOTREACHED("ReadSegments");
395 return NS_ERROR_NOT_IMPLEMENTED
;
398 //----------------------------------------------------------------------------------------
399 NS_IMETHODIMP
FileImpl::Write(const char* aBuf
, PRUint32 aCount
, PRUint32
*aWriteCount
)
400 //----------------------------------------------------------------------------------------
402 NS_PRECONDITION(aBuf
!= nsnull
, "null ptr");
403 NS_PRECONDITION(aWriteCount
!= nsnull
, "null ptr");
408 // Calling PR_Write on stdout is sure suicide.
409 if (mFileDesc
== PR_STDOUT
|| mFileDesc
== PR_STDERR
)
411 std::cout
.write(aBuf
, aCount
);
412 *aWriteCount
= aCount
;
417 return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR
);
419 return NS_ERROR_FAILURE
;
423 nsresult rv
= AllocateBuffers(kOuputBufferSegmentSize
, kOuputBufferMaxSize
);
425 return rv
; // try to write non-buffered?
428 PRUint32 bufOffset
= 0;
429 PRUint32 currentWrite
= 0;
432 if (mWriteCursor
== nsnull
|| mWriteCursor
== mWriteLimit
)
434 char* seg
= mOutBuffer
.AppendNewSegment();
437 // buffer is full, try again
438 InternalFlush(PR_FALSE
);
439 seg
= mOutBuffer
.AppendNewSegment();
441 return NS_ERROR_OUT_OF_MEMORY
;
444 mWriteLimit
= seg
+ mOutBuffer
.GetSegmentSize();
448 currentWrite
= mWriteLimit
- mWriteCursor
;
450 if (aCount
< currentWrite
)
451 currentWrite
= aCount
;
453 memcpy(mWriteCursor
, (aBuf
+ bufOffset
), currentWrite
);
455 mWriteCursor
+= currentWrite
;
457 aCount
-= currentWrite
;
458 bufOffset
+= currentWrite
;
459 *aWriteCount
+= currentWrite
;
466 nsWriteSegmentToFile(nsIInputStream
* in
,
468 const char* fromRawSegment
,
471 PRUint32
*writeCount
)
473 NS_NOTREACHED("nsWriteSegmentToFile");
474 return NS_ERROR_NOT_IMPLEMENTED
;
478 FileImpl::WriteFrom(nsIInputStream
*inStr
, PRUint32 count
, PRUint32
*result
)
480 return inStr
->ReadSegments(nsWriteSegmentToFile
, nsnull
, count
, result
);
484 FileImpl::WriteSegments(nsReadSegmentFun reader
, void * closure
,
485 PRUint32 count
, PRUint32
*result
)
487 NS_NOTREACHED("WriteSegments");
488 return NS_ERROR_NOT_IMPLEMENTED
;
492 FileImpl::IsNonBlocking(PRBool
*aNonBlocking
)
494 *aNonBlocking
= PR_FALSE
;
498 //----------------------------------------------------------------------------------------
499 NS_IMETHODIMP
FileImpl::Tell(PRInt64
* outWhere
)
500 //----------------------------------------------------------------------------------------
502 if (mFileDesc
==PR_STDIN
|| mFileDesc
==PR_STDOUT
|| mFileDesc
==PR_STDERR
|| !mFileDesc
)
503 return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR
);
504 *outWhere
= PR_Seek64(mFileDesc
, 0, PR_SEEK_CUR
);
508 //----------------------------------------------------------------------------------------
509 NS_IMETHODIMP
FileImpl::Close()
510 //----------------------------------------------------------------------------------------
512 if ((mNSPRMode
& PR_RDONLY
) == 0)
513 InternalFlush(PR_FALSE
);
515 if (mFileDesc
==PR_STDIN
|| mFileDesc
==PR_STDOUT
|| mFileDesc
==PR_STDERR
|| !mFileDesc
)
517 if (PR_Close(mFileDesc
) == PR_SUCCESS
)
520 return NS_FILE_RESULT(PR_GetError());
524 //----------------------------------------------------------------------------------------
525 NS_IMETHODIMP
FileImpl::Flush()
526 //----------------------------------------------------------------------------------------
528 // for external callers, this will do a Sync as well as flush buffers.
529 return InternalFlush(PR_TRUE
);
533 //----------------------------------------------------------------------------------------
534 NS_IMETHODIMP
FileImpl::GetAtEOF(PRBool
* outAtEOF
)
535 //----------------------------------------------------------------------------------------
542 //----------------------------------------------------------------------------------------
543 NS_IMETHODIMP
FileImpl::SetAtEOF(PRBool inAtEOF
)
544 //----------------------------------------------------------------------------------------
550 //----------------------------------------------------------------------------------------
551 NS_IMETHODIMP
FileImpl::SetEOF()
552 //----------------------------------------------------------------------------------------
554 NS_NOTYETIMPLEMENTED("FileImpl::SetEOF");
555 return NS_ERROR_NOT_IMPLEMENTED
;
558 //----------------------------------------------------------------------------------------
559 nsresult
FileImpl::AllocateBuffers(PRUint32 segmentSize
, PRUint32 maxBufSize
)
560 //----------------------------------------------------------------------------------------
562 nsresult rv
= mOutBuffer
.Init(segmentSize
, maxBufSize
);
563 if (NS_SUCCEEDED(rv
))
564 mGotBuffers
= PR_TRUE
;
569 // external callers of Flush will have sync get called,
570 // but internal callers just want to flush the buffers to disk.
571 nsresult
FileImpl::InternalFlush(PRBool syncFile
)
574 if (mFileDesc
== PR_STDOUT
|| mFileDesc
== PR_STDERR
)
581 return NS_FILE_RESULT(PR_BAD_DESCRIPTOR_ERROR
);
583 PRInt32 segCount
= mOutBuffer
.GetSegmentCount();
584 PRUint32 segSize
= mOutBuffer
.GetSegmentSize();
586 for (PRInt32 i
= 0; i
< segCount
; i
++)
588 char* seg
= mOutBuffer
.GetSegment(i
);
590 // if it is the last buffer, it may not be completely full.
591 if(i
== (segCount
-1))
592 segSize
= (mWriteCursor
- seg
);
594 PRInt32 bytesWrit
= PR_Write(mFileDesc
, seg
, segSize
);
595 if (bytesWrit
!= (PRInt32
)segSize
)
598 return NS_FILE_RESULT(PR_GetError());
604 mWriteCursor
= nsnull
;
605 mWriteLimit
= nsnull
;
607 // On unix, it seems to fail always.
608 if (syncFile
&& PR_Sync(mFileDesc
) != PR_SUCCESS
)
613 //----------------------------------------------------------------------------------------
614 nsresult
NS_NewTypicalInputFileStream(
615 nsISupports
** aResult
,
616 const nsFileSpec
& inFile
617 /*Default nsprMode == PR_RDONLY*/
618 /*Default accessmode = 0666 (octal)*/)
619 // Factory method to get an nsInputStream from a file, using most common options
620 //----------------------------------------------------------------------------------------
622 // This QueryInterface was needed because NS_NewIOFileStream
623 // does a cast from (void *) to (nsISupports *) thus causing a
624 // vtable problem on Windows, where we really didn't have the proper pointer
625 // to an nsIInputStream, this ensures that we do
626 nsISupports
* supports
;
627 nsIInputStream
* inStr
;
629 nsresult rv
= NS_NewIOFileStream(&supports
, inFile
, PR_RDONLY
, 0666);
632 if (NS_SUCCEEDED(rv
)) {
633 if (NS_SUCCEEDED(supports
->QueryInterface(NS_GET_IID(nsIInputStream
), (void**)&inStr
))) {
636 NS_RELEASE(supports
);
641 //----------------------------------------------------------------------------------------
642 nsresult
NS_NewTypicalOutputFileStream(
643 nsISupports
** aResult
,
644 const nsFileSpec
& inFile
645 /*default nsprMode= (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE)*/
646 /*Default accessMode= 0666 (octal)*/)
647 // Factory method to get an nsOutputStream to a file - most common case.
648 //----------------------------------------------------------------------------------------
650 // This QueryInterface was needed because NS_NewIOFileStream
651 // does a cast from (void *) to (nsISupports *) thus causing a
652 // vtable problem on Windows, where we really didn't have the proper pointer
653 // to an nsIOutputStream, this ensures that we do
655 /* nsISupports * supports;
656 nsIOutputStream * outStr;
658 nsresult rv = NS_NewIOFileStream(
661 (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
665 if (NS_SUCCEEDED(rv)) {
666 if (NS_SUCCEEDED(supports->QueryInterface(NS_GET_IID(nsIOutputStream), (void**)&outStr))) {
669 NS_RELEASE(supports);
674 nsCOMPtr
<nsISupports
> supports
;
675 nsIOutputStream
* outStr
;
677 nsresult rv
= NS_NewIOFileStream(
678 getter_AddRefs(supports
),
680 (PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
),
684 if (NS_SUCCEEDED(rv
)) {
685 if (NS_SUCCEEDED(supports
->QueryInterface(NS_GET_IID(nsIOutputStream
), (void**)&outStr
))) {
691 return NS_NewIOFileStream(
694 (PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
),
699 //----------------------------------------------------------------------------------------
700 NS_COM_OBSOLETE nsresult
NS_NewIOFileStream(
701 nsISupports
** aResult
,
702 const nsFileSpec
& inFile
,
703 PRInt32 nsprMode
/*default = (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE)*/,
704 PRInt32 accessMode
/*Default = 0666 (octal)*/)
705 // Factory method to get an object that implements both nsIInputStream
706 // and nsIOutputStream, associated with a file.
707 //----------------------------------------------------------------------------------------
709 NS_PRECONDITION(aResult
!= nsnull
, "null ptr");
711 return NS_ERROR_NULL_POINTER
;
713 FileImpl
* stream
= new FileImpl(inFile
, nsprMode
, accessMode
);
715 return NS_ERROR_OUT_OF_MEMORY
;
718 PRBool isOpened
= PR_FALSE
;
719 stream
->GetIsOpen(&isOpened
);
723 return NS_ERROR_FAILURE
;
726 *aResult
= (nsISupports
*)(void*)stream
;