1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef _MDB_
7 #  include "mdb.h"
8 #endif
9 
10 #ifndef _MORK_
11 #  include "mork.h"
12 #endif
13 
14 #ifndef _MORKNODE_
15 #  include "morkNode.h"
16 #endif
17 
18 #ifndef _MORKENV_
19 #  include "morkEnv.h"
20 #endif
21 
22 #ifndef _MORKFILE_
23 #  include "morkFile.h"
24 #endif
25 
26 #ifdef MORK_WIN
27 #  include "io.h"
28 #  include <windows.h>
29 #endif
30 
31 #include "mozilla/Unused.h"
32 #include "nsString.h"
33 
34 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
35 
36 // ````` ````` ````` ````` `````
37 // { ===== begin morkNode interface =====
38 
CloseMorkNode(morkEnv * ev)39 /*public virtual*/ void morkFile::CloseMorkNode(
40     morkEnv* ev)  // CloseFile() only if open
41 {
42   if (this->IsOpenNode()) {
43     this->MarkClosing();
44     this->CloseFile(ev);
45     this->MarkShut();
46   }
47 }
48 
49 /*public virtual*/
~morkFile()50 morkFile::~morkFile()  // assert CloseFile() executed earlier
51 {
52   MORK_ASSERT(mFile_Frozen == 0);
53   MORK_ASSERT(mFile_DoTrace == 0);
54   MORK_ASSERT(mFile_IoOpen == 0);
55   MORK_ASSERT(mFile_Active == 0);
56 }
57 
58 /*public non-poly*/
morkFile(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap)59 morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
60                    nsIMdbHeap* ioSlotHeap)
61     : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0),
62       mFile_Frozen(0),
63       mFile_DoTrace(0),
64       mFile_IoOpen(0),
65       mFile_Active(0)
66 
67       ,
68       mFile_SlotHeap(0),
69       mFile_Name(0),
70       mFile_Thief(0) {
71   if (ev->Good()) {
72     if (ioSlotHeap) {
73       nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap);
74       if (ev->Good()) mNode_Derived = morkDerived_kFile;
75     } else
76       ev->NilPointerError();
77   }
78 }
79 
NS_IMPL_ISUPPORTS_INHERITED(morkFile,morkObject,nsIMdbFile)80 NS_IMPL_ISUPPORTS_INHERITED(morkFile, morkObject, nsIMdbFile)
81 /*public non-poly*/ void morkFile::CloseFile(
82     morkEnv* ev)  // called by CloseMorkNode();
83 {
84   if (this->IsNode()) {
85     mFile_Frozen = 0;
86     mFile_DoTrace = 0;
87     mFile_IoOpen = 0;
88     mFile_Active = 0;
89 
90     if (mFile_Name) this->SetFileName(ev, nullptr);
91 
92     nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mFile_SlotHeap);
93     nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mFile_Thief);
94 
95     this->MarkShut();
96   } else
97     this->NonNodeError(ev);
98 }
99 
100 // } ===== end morkNode methods =====
101 // ````` ````` ````` ````` `````
102 
OpenOldFile(morkEnv * ev,nsIMdbHeap * ioHeap,const PathChar * inFilePath,mork_bool inFrozen)103 /*static*/ morkFile* morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap,
104                                            const PathChar* inFilePath,
105                                            mork_bool inFrozen)
106 // Choose some subclass of morkFile to instantiate, in order to read
107 // (and write if not frozen) the file known by inFilePath.  The file
108 // returned should be open and ready for use, and presumably positioned
109 // at the first byte position of the file.  The exact manner in which
110 // files must be opened is considered a subclass specific detail, and
111 // other portions or Mork source code don't want to know how it's done.
112 {
113   return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen);
114 }
115 
CreateNewFile(morkEnv * ev,nsIMdbHeap * ioHeap,const PathChar * inFilePath)116 /*static*/ morkFile* morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap,
117                                              const PathChar* inFilePath)
118 // Choose some subclass of morkFile to instantiate, in order to read
119 // (and write if not frozen) the file known by inFilePath.  The file
120 // returned should be created and ready for use, and presumably positioned
121 // at the first byte position of the file.  The exact manner in which
122 // files must be opened is considered a subclass specific detail, and
123 // other portions or Mork source code don't want to know how it's done.
124 {
125   return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath);
126 }
127 
NewMissingIoError(morkEnv * ev) const128 void morkFile::NewMissingIoError(morkEnv* ev) const {
129   ev->NewError("file missing io");
130 }
131 
NonFileTypeError(morkEnv * ev)132 /*static*/ void morkFile::NonFileTypeError(morkEnv* ev) {
133   ev->NewError("non morkFile");
134 }
135 
NilSlotHeapError(morkEnv * ev)136 /*static*/ void morkFile::NilSlotHeapError(morkEnv* ev) {
137   ev->NewError("nil mFile_SlotHeap");
138 }
139 
NilFileNameError(morkEnv * ev)140 /*static*/ void morkFile::NilFileNameError(morkEnv* ev) {
141   ev->NewError("nil mFile_Name");
142 }
143 
SetThief(morkEnv * ev,nsIMdbFile * ioThief)144 void morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief) {
145   nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief);
146 }
147 
SetFileName(morkEnv * ev,const PathChar * inName)148 void morkFile::SetFileName(morkEnv* ev,
149                            const PathChar* inName)  // inName can be nil
150 {
151   nsIMdbHeap* heap = mFile_SlotHeap;
152   if (heap) {
153     PathChar* name = mFile_Name;
154     if (name) {
155       mFile_Name = 0;
156       ev->FreeString(heap, name);
157     }
158     if (ev->Good() && inName) mFile_Name = ev->CopyString(heap, inName);
159   } else
160     this->NilSlotHeapError(ev);
161 }
162 
NewFileDownError(morkEnv * ev) const163 void morkFile::NewFileDownError(morkEnv* ev) const
164 // call NewFileDownError() when either IsOpenAndActiveFile()
165 // is false, or when IsOpenActiveAndMutableFile() is false.
166 {
167   if (this->IsOpenNode()) {
168     if (this->FileActive()) {
169       if (this->FileFrozen()) {
170         ev->NewError("file frozen");
171       } else
172         ev->NewError("unknown file problem");
173     } else
174       ev->NewError("file not active");
175   } else
176     ev->NewError("file not open");
177 }
178 
NewFileErrnoError(morkEnv * ev) const179 void morkFile::NewFileErrnoError(morkEnv* ev) const
180 // call NewFileErrnoError() to convert std C errno into AB fault
181 {
182   const char* errnoString = strerror(errno);
183   ev->NewError(errnoString);  // maybe pass value of strerror() instead
184 }
185 
186 // ````` ````` ````` ````` newlines ````` ````` ````` `````
187 
188 #if defined(MORK_MAC)
189 static const char morkFile_kNewlines[] =
190     "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015";
191 #  define morkFile_kNewlinesCount 16
192 #else
193 #  if defined(MORK_WIN)
194 static const char morkFile_kNewlines[] =
195     "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012";
196 #    define morkFile_kNewlinesCount 8
197 #  else
198 #    ifdef MORK_UNIX
199 static const char morkFile_kNewlines[] =
200     "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012";
201 #      define morkFile_kNewlinesCount 16
202 #    endif /* MORK_UNIX */
203 #  endif   /* MORK_WIN */
204 #endif     /* MORK_MAC */
205 
WriteNewlines(morkEnv * ev,mork_count inNewlines)206 mork_size morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines)
207 // WriteNewlines() returns the number of bytes written.
208 {
209   mork_size outSize = 0;
210   while (inNewlines && ev->Good())  // more newlines to write?
211   {
212     mork_u4 quantum = inNewlines;
213     if (quantum > morkFile_kNewlinesCount) quantum = morkFile_kNewlinesCount;
214 
215     mork_size quantumSize = quantum * mork_kNewlineSize;
216     mdb_size bytesWritten;
217     this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten);
218     outSize += quantumSize;
219     inNewlines -= quantum;
220   }
221   return outSize;
222 }
223 
224 NS_IMETHODIMP
Eof(nsIMdbEnv * mev,mdb_pos * outPos)225 morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos) {
226   nsresult outErr = NS_OK;
227   mdb_pos pos = -1;
228   morkEnv* ev = morkEnv::FromMdbEnv(mev);
229   pos = Length(ev);
230   outErr = ev->AsErr();
231   if (outPos) *outPos = pos;
232   return outErr;
233 }
234 
235 NS_IMETHODIMP
Get(nsIMdbEnv * mev,void * outBuf,mdb_size inSize,mdb_pos inPos,mdb_size * outActualSize)236 morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize, mdb_pos inPos,
237               mdb_size* outActualSize) {
238   nsresult rv = NS_OK;
239   morkEnv* ev = morkEnv::FromMdbEnv(mev);
240   if (ev) {
241     mdb_pos outPos;
242     Seek(mev, inPos, &outPos);
243     if (ev->Good()) rv = Read(mev, outBuf, inSize, outActualSize);
244   }
245   return rv;
246 }
247 
248 NS_IMETHODIMP
Put(nsIMdbEnv * mev,const void * inBuf,mdb_size inSize,mdb_pos inPos,mdb_size * outActualSize)249 morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize, mdb_pos inPos,
250               mdb_size* outActualSize) {
251   nsresult outErr = NS_OK;
252   *outActualSize = 0;
253   morkEnv* ev = morkEnv::FromMdbEnv(mev);
254   if (ev) {
255     mdb_pos outPos;
256 
257     Seek(mev, inPos, &outPos);
258     if (ev->Good()) Write(mev, inBuf, inSize, outActualSize);
259     outErr = ev->AsErr();
260   }
261   return outErr;
262 }
263 
264 // { ----- begin path methods -----
265 NS_IMETHODIMP
Path(nsIMdbEnv * mev,mdbYarn * outFilePath)266 morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath) {
267   nsresult outErr = NS_OK;
268   if (outFilePath) outFilePath->mYarn_Fill = 0;
269   morkEnv* ev = morkEnv::FromMdbEnv(mev);
270   if (ev) {
271     ev->StringToYarn(GetFileNameString(), outFilePath);
272     outErr = ev->AsErr();
273   }
274   return outErr;
275 }
276 
277 // } ----- end path methods -----
278 
279 // { ----- begin replacement methods -----
280 
281 NS_IMETHODIMP
Thief(nsIMdbEnv * mev,nsIMdbFile ** acqThief)282 morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief) {
283   nsresult outErr = NS_OK;
284   nsIMdbFile* outThief = 0;
285   morkEnv* ev = morkEnv::FromMdbEnv(mev);
286   if (ev) {
287     outThief = GetThief();
288     NS_IF_ADDREF(outThief);
289     outErr = ev->AsErr();
290   }
291   if (acqThief) *acqThief = outThief;
292   return outErr;
293 }
294 
295 // } ----- end replacement methods -----
296 
297 // { ----- begin versioning methods -----
298 
299 // ````` ````` ````` ````` `````
300 // { ===== begin morkNode interface =====
301 
CloseMorkNode(morkEnv * ev)302 /*public virtual*/ void morkStdioFile::CloseMorkNode(
303     morkEnv* ev)  // CloseStdioFile() only if open
304 {
305   if (this->IsOpenNode()) {
306     this->MarkClosing();
307     this->CloseStdioFile(ev);
308     this->MarkShut();
309   }
310 }
311 
312 /*public virtual*/
~morkStdioFile()313 morkStdioFile::~morkStdioFile()  // assert CloseStdioFile() executed earlier
314 {
315   if (mStdioFile_File) CloseStdioFile(mMorkEnv);
316   MORK_ASSERT(mStdioFile_File == 0);
317 }
318 
CloseStdioFile(morkEnv * ev)319 /*public non-poly*/ void morkStdioFile::CloseStdioFile(
320     morkEnv* ev)  // called by CloseMorkNode();
321 {
322   if (this->IsNode()) {
323     if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) {
324       this->CloseStdio(ev);
325     }
326 
327     mStdioFile_File = 0;
328 
329     this->CloseFile(ev);
330     this->MarkShut();
331   } else
332     this->NonNodeError(ev);
333 }
334 
335 // } ===== end morkNode methods =====
336 // ````` ````` ````` ````` `````
337 
338 // compatible with the morkFile::MakeFile() entry point
339 
OpenOldStdioFile(morkEnv * ev,nsIMdbHeap * ioHeap,const PathChar * inFilePath,mork_bool inFrozen)340 /*static*/ morkStdioFile* morkStdioFile::OpenOldStdioFile(
341     morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath,
342     mork_bool inFrozen) {
343   morkStdioFile* outFile = 0;
344   if (ioHeap && inFilePath) {
345     const char* mode = (inFrozen) ? "rb" : "rb+";
346     outFile = new (*ioHeap, ev)
347         morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode);
348 
349     if (outFile) {
350       outFile->SetFileFrozen(inFrozen);
351     }
352   } else
353     ev->NilPointerError();
354 
355   return outFile;
356 }
357 
CreateNewStdioFile(morkEnv * ev,nsIMdbHeap * ioHeap,const PathChar * inFilePath)358 /*static*/ morkStdioFile* morkStdioFile::CreateNewStdioFile(
359     morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath) {
360   morkStdioFile* outFile = 0;
361   if (ioHeap && inFilePath) {
362     const char* mode = "wb+";
363     outFile = new (*ioHeap, ev)
364         morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode);
365   } else
366     ev->NilPointerError();
367 
368   return outFile;
369 }
370 
371 NS_IMETHODIMP
BecomeTrunk(nsIMdbEnv * ev)372 morkStdioFile::BecomeTrunk(nsIMdbEnv* ev)
373 // If this file is a file version branch created by calling AcquireBud(),
374 // BecomeTrunk() causes this file's content to replace the original
375 // file's content, typically by assuming the original file's identity.
376 {
377   return Flush(ev);
378 }
379 
380 NS_IMETHODIMP
AcquireBud(nsIMdbEnv * mdbev,nsIMdbHeap * ioHeap,nsIMdbFile ** acquiredFile)381 morkStdioFile::AcquireBud(nsIMdbEnv* mdbev, nsIMdbHeap* ioHeap,
382                           nsIMdbFile** acquiredFile)
383 // AcquireBud() starts a new "branch" version of the file, empty of content,
384 // so that a new version of the file can be written.  This new file
385 // can later be told to BecomeTrunk() the original file, so the branch
386 // created by budding the file will replace the original file.  Some
387 // file subclasses might initially take the unsafe but expedient
388 // approach of simply truncating this file down to zero length, and
389 // then returning the same morkFile pointer as this, with an extra
390 // reference count increment.  Note that the caller of AcquireBud() is
391 // expected to eventually call CutStrongRef() on the returned file
392 // in order to release the strong reference.  High quality versions
393 // of morkFile subclasses will create entirely new files which later
394 // are renamed to become the old file, so that better transactional
395 // behavior is exhibited by the file, so crashes protect old files.
396 // Note that AcquireBud() is an illegal operation on readonly files.
397 {
398   NS_ENSURE_ARG(acquiredFile);
399   MORK_USED_1(ioHeap);
400   nsresult rv = NS_OK;
401   morkFile* outFile = 0;
402   morkEnv* ev = morkEnv::FromMdbEnv(mdbev);
403 
404   if (this->IsOpenAndActiveFile()) {
405     FILE* file = (FILE*)mStdioFile_File;
406     if (file) {
407       //#ifdef MORK_WIN
408       //      truncate(file, /*eof*/ 0);
409       //#else /*MORK_WIN*/
410       PathChar* name = mFile_Name;
411       if (name) {
412         if (MORK_FILECLOSE(file) >= 0) {
413           this->SetFileActive(morkBool_kFalse);
414           this->SetFileIoOpen(morkBool_kFalse);
415           mStdioFile_File = 0;
416 
417           file = MORK_FILEOPEN(
418               name, "wb+");  // open for write, discarding old content
419           if (file) {
420             mStdioFile_File = file;
421             this->SetFileActive(morkBool_kTrue);
422             this->SetFileIoOpen(morkBool_kTrue);
423             this->SetFileFrozen(morkBool_kFalse);
424           } else
425             this->new_stdio_file_fault(ev);
426         } else
427           this->new_stdio_file_fault(ev);
428       } else
429         this->NilFileNameError(ev);
430 
431       //#endif /*MORK_WIN*/
432 
433       if (ev->Good() && this->AddStrongRef(ev->AsMdbEnv())) {
434         outFile = this;
435         AddRef();
436       }
437     } else if (mFile_Thief) {
438       rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile);
439     } else
440       this->NewMissingIoError(ev);
441   } else
442     this->NewFileDownError(ev);
443 
444   *acquiredFile = outFile;
445   return rv;
446 }
447 
Length(morkEnv * ev) const448 mork_pos morkStdioFile::Length(morkEnv* ev) const {
449   mork_pos outPos = 0;
450 
451   if (this->IsOpenAndActiveFile()) {
452     FILE* file = (FILE*)mStdioFile_File;
453     if (file) {
454       long start = MORK_FILETELL(file);
455       if (start >= 0) {
456         long fore = MORK_FILESEEK(file, 0, SEEK_END);
457         if (fore >= 0) {
458           long eof = MORK_FILETELL(file);
459           if (eof >= 0) {
460             long back = MORK_FILESEEK(file, start, SEEK_SET);
461             if (back >= 0)
462               outPos = eof;
463             else
464               this->new_stdio_file_fault(ev);
465           } else
466             this->new_stdio_file_fault(ev);
467         } else
468           this->new_stdio_file_fault(ev);
469       } else
470         this->new_stdio_file_fault(ev);
471     } else if (mFile_Thief)
472       mFile_Thief->Eof(ev->AsMdbEnv(), &outPos);
473     else
474       this->NewMissingIoError(ev);
475   } else
476     this->NewFileDownError(ev);
477 
478   return outPos;
479 }
480 
481 NS_IMETHODIMP
Tell(nsIMdbEnv * ev,mork_pos * outPos) const482 morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos* outPos) const {
483   nsresult rv = NS_OK;
484   NS_ENSURE_ARG(outPos);
485   morkEnv* mev = morkEnv::FromMdbEnv(ev);
486   if (this->IsOpenAndActiveFile()) {
487     FILE* file = (FILE*)mStdioFile_File;
488     if (file) {
489       long where = MORK_FILETELL(file);
490       if (where >= 0)
491         *outPos = where;
492       else
493         this->new_stdio_file_fault(mev);
494     } else if (mFile_Thief)
495       mFile_Thief->Tell(ev, outPos);
496     else
497       this->NewMissingIoError(mev);
498   } else
499     this->NewFileDownError(mev);
500 
501   return rv;
502 }
503 
504 NS_IMETHODIMP
Read(nsIMdbEnv * ev,void * outBuf,mork_size inSize,mork_num * outCount)505 morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize,
506                     mork_num* outCount) {
507   nsresult rv = NS_OK;
508   morkEnv* mev = morkEnv::FromMdbEnv(ev);
509   if (this->IsOpenAndActiveFile()) {
510     FILE* file = (FILE*)mStdioFile_File;
511     if (file) {
512       long count = (long)MORK_FILEREAD(outBuf, inSize, file);
513       if (count >= 0) {
514         *outCount = (mork_num)count;
515       } else
516         this->new_stdio_file_fault(mev);
517     } else if (mFile_Thief)
518       mFile_Thief->Read(ev, outBuf, inSize, outCount);
519     else
520       this->NewMissingIoError(mev);
521   } else
522     this->NewFileDownError(mev);
523 
524   return rv;
525 }
526 
527 NS_IMETHODIMP
Seek(nsIMdbEnv * mdbev,mork_pos inPos,mork_pos * aOutPos)528 morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos* aOutPos) {
529   mork_pos outPos = 0;
530   nsresult rv = NS_OK;
531   morkEnv* ev = morkEnv::FromMdbEnv(mdbev);
532 
533   if (this->IsOpenOrClosingNode() && this->FileActive()) {
534     FILE* file = (FILE*)mStdioFile_File;
535     if (file) {
536       long where = MORK_FILESEEK(file, inPos, SEEK_SET);
537       if (where >= 0)
538         outPos = inPos;
539       else
540         this->new_stdio_file_fault(ev);
541     } else if (mFile_Thief)
542       mFile_Thief->Seek(mdbev, inPos, aOutPos);
543     else
544       this->NewMissingIoError(ev);
545   } else
546     this->NewFileDownError(ev);
547 
548   *aOutPos = outPos;
549   return rv;
550 }
551 
552 NS_IMETHODIMP
Write(nsIMdbEnv * mdbev,const void * inBuf,mork_size inSize,mork_size * aOutSize)553 morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize,
554                      mork_size* aOutSize) {
555   mork_num outCount = 0;
556   nsresult rv = NS_OK;
557   morkEnv* ev = morkEnv::FromMdbEnv(mdbev);
558   if (this->IsOpenActiveAndMutableFile()) {
559     FILE* file = (FILE*)mStdioFile_File;
560     if (file) {
561       mozilla::Unused << fwrite(inBuf, 1, inSize, file);
562       if (!ferror(file))
563         outCount = inSize;
564       else
565         this->new_stdio_file_fault(ev);
566     } else if (mFile_Thief)
567       mFile_Thief->Write(mdbev, inBuf, inSize, &outCount);
568     else
569       this->NewMissingIoError(ev);
570   } else
571     this->NewFileDownError(ev);
572 
573   *aOutSize = outCount;
574   return rv;
575 }
576 
577 NS_IMETHODIMP
Flush(nsIMdbEnv * mdbev)578 morkStdioFile::Flush(nsIMdbEnv* mdbev) {
579   morkEnv* ev = morkEnv::FromMdbEnv(mdbev);
580   if (this->IsOpenOrClosingNode() && this->FileActive()) {
581     FILE* file = (FILE*)mStdioFile_File;
582     if (file) {
583       MORK_FILEFLUSH(file);
584 
585     } else if (mFile_Thief)
586       mFile_Thief->Flush(mdbev);
587     else
588       this->NewMissingIoError(ev);
589   } else
590     this->NewFileDownError(ev);
591   return NS_OK;
592 }
593 
594 // ````` ````` ````` `````   ````` ````` ````` `````
595 // protected: // protected non-poly morkStdioFile methods
596 
new_stdio_file_fault(morkEnv * ev) const597 void morkStdioFile::new_stdio_file_fault(morkEnv* ev) const {
598   FILE* file = (FILE*)mStdioFile_File;
599 
600   int copyErrno = errno;  // facilitate seeing error in debugger
601 
602   //  bunch of stuff not ported here
603   if (!copyErrno && file) {
604     copyErrno = ferror(file);
605     errno = copyErrno;
606   }
607 
608   this->NewFileErrnoError(ev);
609 }
610 
611 // ````` ````` ````` `````   ````` ````` ````` `````
612 // public: // public non-poly morkStdioFile methods
613 
614 /*public non-poly*/
morkStdioFile(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap)615 morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
616                              nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
617     : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) {
618   if (ev->Good()) mNode_Derived = morkDerived_kStdioFile;
619 }
620 
morkStdioFile(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap,const PathChar * inName,const char * inMode)621 morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
622                              nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
623                              const PathChar* inName, const char* inMode)
624     // calls OpenStdio() after construction
625     : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) {
626   if (ev->Good()) this->OpenStdio(ev, inName, inMode);
627 }
628 
morkStdioFile(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap,void * ioFile,const PathChar * inName,mork_bool inFrozen)629 morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
630                              nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
631                              void* ioFile, const PathChar* inName,
632                              mork_bool inFrozen)
633     // calls UseStdio() after construction
634     : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) {
635   if (ev->Good()) this->UseStdio(ev, ioFile, inName, inFrozen);
636 }
637 
OpenStdio(morkEnv * ev,const PathChar * inName,const char * inMode)638 void morkStdioFile::OpenStdio(morkEnv* ev, const PathChar* inName,
639                               const char* inMode)
640 // Open a new FILE with name inName, using mode flags from inMode.
641 {
642   if (ev->Good()) {
643     if (!inMode) inMode = "";
644 
645     mork_bool frozen = (*inMode == 'r');  // cursory attempt to note readonly
646 
647     if (this->IsOpenNode()) {
648       if (!this->FileActive()) {
649         this->SetFileIoOpen(morkBool_kFalse);
650         if (inName && *inName) {
651           this->SetFileName(ev, inName);
652           if (ev->Good()) {
653             FILE* file = MORK_FILEOPEN(inName, inMode);
654             if (file) {
655               mStdioFile_File = file;
656               this->SetFileActive(morkBool_kTrue);
657               this->SetFileIoOpen(morkBool_kTrue);
658               this->SetFileFrozen(frozen);
659             } else
660               this->new_stdio_file_fault(ev);
661           }
662         } else
663           ev->NewError("no file name");
664       } else
665         ev->NewError("file already active");
666     } else
667       this->NewFileDownError(ev);
668   }
669 }
670 
UseStdio(morkEnv * ev,void * ioFile,const PathChar * inName,mork_bool inFrozen)671 void morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const PathChar* inName,
672                              mork_bool inFrozen)
673 // Use an existing file, like stdin/stdout/stderr, which should not
674 // have the io stream closed when the file is closed.  The ioFile
675 // parameter must actually be of type FILE (but we don't want to make
676 // this header file include the stdio.h header file).
677 {
678   if (ev->Good()) {
679     if (this->IsOpenNode()) {
680       if (!this->FileActive()) {
681         if (ioFile) {
682           this->SetFileIoOpen(morkBool_kFalse);
683           this->SetFileName(ev, inName);
684           if (ev->Good()) {
685             mStdioFile_File = ioFile;
686             this->SetFileActive(morkBool_kTrue);
687             this->SetFileFrozen(inFrozen);
688           }
689         } else
690           ev->NilPointerError();
691       } else
692         ev->NewError("file already active");
693     } else
694       this->NewFileDownError(ev);
695   }
696 }
697 
CloseStdio(morkEnv * ev)698 void morkStdioFile::CloseStdio(morkEnv* ev)
699 // Close the stream io if both and FileActive() and FileIoOpen(), but
700 // this does not close this instances (like CloseStdioFile() does).
701 // If stream io was made active by means of calling UseStdio(),
702 // then this method does little beyond marking the stream inactive
703 // because FileIoOpen() is false.
704 {
705   if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) {
706     FILE* file = (FILE*)mStdioFile_File;
707     if (MORK_FILECLOSE(file) < 0) this->new_stdio_file_fault(ev);
708 
709     mStdioFile_File = 0;
710     this->SetFileActive(morkBool_kFalse);
711     this->SetFileIoOpen(morkBool_kFalse);
712   }
713 }
714 
715 NS_IMETHODIMP
Steal(nsIMdbEnv * ev,nsIMdbFile * ioThief)716 morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief)
717 // If this file is a file version branch created by calling AcquireBud(),
718 // BecomeTrunk() causes this file's content to replace the original
719 // file's content, typically by assuming the original file's identity.
720 {
721   morkEnv* mev = morkEnv::FromMdbEnv(ev);
722   if (mStdioFile_File && FileActive() && FileIoOpen()) {
723     FILE* file = (FILE*)mStdioFile_File;
724     if (MORK_FILECLOSE(file) < 0) new_stdio_file_fault(mev);
725 
726     mStdioFile_File = 0;
727   }
728   SetThief(mev, ioThief);
729   return NS_OK;
730 }
731 
732 #if defined(MORK_WIN)
733 
mork_fileflush(FILE * file)734 void mork_fileflush(FILE* file) { fflush(file); }
735 
736 #endif /*MORK_WIN*/
737 
738 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
739