1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 /**
40  * @file FFStream.cpp
41  * Formatted File Stream base class
42  */
43 
44 #include "FFStream.hpp"
45 
46 namespace gpstk
47 {
48    FFStream ::
FFStream()49    FFStream()
50          : recordNumber(0)
51    {
52    }
53 
54 
55    FFStream ::
~FFStream()56    ~FFStream()
57    {
58    }
59 
60 
61    FFStream ::
FFStream(const char * fn,std::ios::openmode mode)62    FFStream( const char* fn,
63              std::ios::openmode mode )
64          : recordNumber(0),
65            filename(fn)
66    {
67          // Note that this will call FFStream::open, not the child
68          // class.  Virtual function pointer tables aren't populated
69          // until the end of the constructor so the child class'
70          // open() method won't be known at this point.
71          // https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors
72          // As such, child classes should implement their own init()
73          // methods to do any additional processing that is normally
74          // done in open() and call that in the constructor as well as
75          // their own open() methods.
76          // see open() comments for more.
77       open(fn, mode);
78    }
79 
80 
81    FFStream ::
FFStream(const std::string & fn,std::ios::openmode mode)82    FFStream( const std::string& fn,
83              std::ios::openmode mode )
84          : recordNumber(0),
85            filename(fn)
86    {
87       open(fn, mode);
88    }
89 
90 
91    void FFStream ::
open(const std::string & fn,std::ios::openmode mode)92    open( const std::string& fn,
93          std::ios::openmode mode )
94    {
95       open( fn.c_str(), mode );
96    }
97 
98 
99    void FFStream ::
open(const char * fn,std::ios::openmode mode)100    open( const char* fn, std::ios::openmode mode )
101    {
102          // Child classes should never do anything more in open() than
103          // call a class-specific init function and the parent open()
104          // method.  In this case we are calling init() first because
105          // it closes the stream if it's already open, which obviously
106          // shouldn't be done AFTER the new stream is open.  Child
107          // classes typically will want to do their initialization
108          // AFTER the parent.
109       init(fn, mode);
110       std::fstream::open(fn, mode);
111    }  // End of method 'FFStream::open()'
112 
113 
114    void FFStream ::
init(const char * fn,std::ios::openmode mode)115    init( const char* fn, std::ios::openmode mode )
116    {
117       close();
118       clear();
119       filename = std::string(fn);
120       recordNumber = 0;
121    }  // End of method 'FFStream::open()'
122 
123 
124    bool FFStream ::
isFFStream(std::istream & i)125    isFFStream(std::istream& i)
126    {
127       try
128       {
129          (void)dynamic_cast<FFStream&>(i);
130       }
131       catch(...)
132       {
133          return false;
134       }
135 
136       return true;
137    }
138 
139 
140    void FFStream ::
dumpState(std::ostream & s) const141    dumpState(std::ostream& s) const
142    {
143       s << "filename:" << filename
144         << ", recordNumber:" << recordNumber;
145       s << ", exceptions:";
146 
147       if (exceptions() & std::ios::badbit)  s << "bad ";
148       if (exceptions() & std::ios::failbit) s << "fail ";
149       if (exceptions() & std::ios::eofbit)  s << "eof ";
150       if (exceptions() == 0) s << "none";
151 
152       s << ", rdstate:";
153 
154       if (rdstate() & std::ios::badbit)  s << "bad ";
155       if (rdstate() & std::ios::failbit) s << "fail ";
156       if (rdstate() & std::ios::eofbit)  s << "eof ";
157       if (rdstate() == 0)  s << "none";
158       s << std::endl;
159    }  // End of method 'FFStream::dumpState()'
160 
161 
tryFFStreamGet(FFData & rec)162    void FFStream::tryFFStreamGet(FFData& rec)
163    {
164          // JMK 2015/12/07 - some implementations of streams will
165          // raise exceptions in tellg if eofbit is set but not
166          // failbit.  This attempts to work around this situation and
167          // make FFStream work more like one would expect, i.e. don't
168          // fail until the failbit is set.
169       if (rdstate() == std::ios::eofbit)
170          clear(); // clear ONLY if eofbit is the only state flag set
171          // Mark where we start in case there is an error.
172       long initialPosition = tellg();
173       unsigned long initialRecordNumber = recordNumber;
174       clear();
175 
176       try
177       {
178          try
179          {
180             rec.reallyGetRecord(*this);
181             recordNumber++;
182          }
183          catch (EndOfFile& e)
184          {
185                // EOF - do nothing - eof causes fail() to be set which
186                // is handled by std::fstream
187             e.addText("In record " +
188                       gpstk::StringUtils::asString(recordNumber));
189             e.addText("In file " + filename);
190             e.addLocation(FILE_LOCATION);
191             mostRecentException = e;
192          }
193          catch (std::exception &e)
194          {
195             mostRecentException = FFStreamError("std::exception thrown: " +
196                                                 std::string(e.what()));
197             mostRecentException.addText("In record " +
198                                         gpstk::StringUtils::asString(recordNumber));
199             mostRecentException.addText("In file " + filename);
200             mostRecentException.addLocation(FILE_LOCATION);
201             clear();
202             seekg(initialPosition);
203             recordNumber = initialRecordNumber;
204             setstate(std::ios::failbit);
205             conditionalThrow();
206          }
207          catch (gpstk::StringUtils::StringException& e)
208          {
209             e.addText("In record " +
210                       gpstk::StringUtils::asString(recordNumber));
211             e.addText("In file " + filename);
212             e.addLocation(FILE_LOCATION);
213             mostRecentException = e;
214             clear();
215             seekg(initialPosition);
216             recordNumber = initialRecordNumber;
217             setstate(std::ios::failbit);
218             conditionalThrow();
219          }
220             // catches some errors we can encounter
221          catch (FFStreamError& e)
222          {
223             e.addText("In record " +
224                       gpstk::StringUtils::asString(recordNumber));
225             e.addText("In file " + filename);
226             e.addLocation(FILE_LOCATION);
227             mostRecentException = e;
228             clear();
229             seekg(initialPosition);
230             recordNumber = initialRecordNumber;
231             setstate(std::ios::failbit);
232             conditionalThrow();
233 
234          }
235       }
236          // this is if you throw an FFStream error in the above catch
237          // block because the catch(...) below will mask it otherwise.
238          // This also takes care of catching StringExceptions
239       catch (gpstk::Exception &e)
240       {
241          GPSTK_RETHROW(e);
242       }
243       catch (std::ifstream::failure &e)
244       {
245             // setting failbit when catching FFStreamError can cause
246             // this exception to be thrown. in this case, we don't want
247             // to lose the exception info so only make a new exception
248             // if this isn't a fail() case
249          if (!fail())
250          {
251             mostRecentException = FFStreamError("ifstream::failure thrown: " +
252                                                 std::string(e.what()));
253             mostRecentException.addText("In file " + filename);
254             mostRecentException.addLocation(FILE_LOCATION);
255          }
256          conditionalThrow();
257       }
258       catch (std::exception &e)
259       {
260          mostRecentException = FFStreamError("std::exception thrown: " +
261                                              std::string(e.what()));
262          mostRecentException.addText("In file " + filename);
263          mostRecentException.addLocation(FILE_LOCATION);
264          setstate(std::ios::failbit);
265          conditionalThrow();
266       }
267       catch (...)
268       {
269          mostRecentException = FFStreamError("Unknown exception thrown");
270          mostRecentException.addText("In file " + filename);
271          mostRecentException.addLocation(FILE_LOCATION);
272          setstate(std::ios::failbit);
273          conditionalThrow();
274       }
275 
276    }  // End of method 'FFStream::tryFFStreamGet()'
277 
278 
279 
280       // the crazy double try block is so that no gpstk::Exception throws
281       // get masked, allowing all exception information (line numbers, text,
282       // etc) to be retained.
283    void FFStream ::
tryFFStreamPut(const FFData & rec)284    tryFFStreamPut(const FFData& rec)
285    {
286          // Mark where we start in case there is an error.
287       long initialPosition = tellg();
288       unsigned long initialRecordNumber = recordNumber;
289       clear();
290 
291       try
292       {
293          try
294          {
295             rec.reallyPutRecord(*this);
296             recordNumber++;
297          }
298          catch (std::exception &e)
299          {
300                // if this is a stream failure, don't mask it and let the
301                // later catch block handle it
302             if (dynamic_cast<std::ifstream::failure*>(&e))
303                throw;
304 
305                // the catch(FFStreamError) below will add file information
306                // to this exception
307             mostRecentException = FFStreamError("std::exception thrown: " +
308                                                 std::string(e.what()));
309             mostRecentException.addLocation(FILE_LOCATION);
310             setstate(std::ios::failbit);
311             conditionalThrow();
312          }
313          catch (gpstk::StringUtils::StringException& e)
314          {
315             e.addText("In record " +
316                       gpstk::StringUtils::asString(recordNumber));
317             e.addText("In file " + filename);
318             e.addLocation(FILE_LOCATION);
319             mostRecentException = e;
320             seekg(initialPosition);
321             recordNumber = initialRecordNumber;
322             setstate(std::ios::failbit);
323             conditionalThrow();
324          }
325             // catches some errors we can encounter
326          catch (FFStreamError& e)
327          {
328             e.addText("In record " +
329                       gpstk::StringUtils::asString(recordNumber));
330             e.addText("In file " + filename);
331             e.addLocation(FILE_LOCATION);
332             mostRecentException = e;
333             seekg(initialPosition);
334             recordNumber = initialRecordNumber;
335             setstate(std::ios::failbit);
336             conditionalThrow();
337          }
338       }
339          // this is if you throw an FFStream error in the above catch
340          // block because the catch(...) below will mask it otherwise.
341          // This also takes care of catching StringExceptions
342       catch (gpstk::Exception &e)
343       {
344          GPSTK_RETHROW(e);
345       }
346       catch (std::ifstream::failure &e)
347       {
348             // setting failbit when catching FFStreamError can cause
349             // this exception to be thrown. in this case, we don't want
350             // to lose the exception info so only make a new exception
351             // if this isn't a fail() case
352          if (!fail())
353          {
354             mostRecentException = FFStreamError("ifstream::failure thrown: " +
355                                                 std::string(e.what()));
356             mostRecentException.addText("In file " + filename);
357             mostRecentException.addLocation(FILE_LOCATION);
358          }
359          conditionalThrow();
360       }
361       catch (std::exception &e)
362       {
363          mostRecentException = FFStreamError("std::exception thrown: " +
364                                              std::string(e.what()));
365          mostRecentException.addText("In file " + filename);
366          mostRecentException.addLocation(FILE_LOCATION);
367          setstate(std::ios::failbit);
368          conditionalThrow();
369       }
370       catch (...)
371       {
372          mostRecentException = FFStreamError("Unknown exception thrown");
373          mostRecentException.addText("In file " + filename);
374          mostRecentException.addLocation(FILE_LOCATION);
375          setstate(std::ios::failbit);
376          conditionalThrow();
377       }
378 
379    }  // End of method 'FFStream::tryFFStreamPut()'
380 
381 
382 
383 }  // End of namespace gpstk
384