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