1 /************************************************************************** 2 3 Extension to ErrnoException specific to file I/O errors 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 **************************************************************************/ 15 16 module ocean.io.FileException; 17 18 19 import ocean.meta.types.Qualifiers; 20 21 import ocean.sys.ErrnoException; 22 import core.stdc.errno; 23 24 /************************************************************************** 25 26 Exception class that checks error status on both file descriptor 27 and via global errno 28 29 **************************************************************************/ 30 31 class FileException : ErrnoException 32 { 33 import core.stdc.stdio: FILE, ferror, feof, clearerr; 34 35 /// Make ErrnoException's enforce available 36 public alias ErrnoException.enforce enforce; 37 38 /************************************************************************** 39 40 Enforces success of file I/O operation 41 42 Params: 43 ok = I/O expression that returns `false` on failure 44 filename = filename that was used for the I/O (only used in 45 message formatting) 46 handle = file handle that was used for the I/O (can be null) 47 file = file where the enforce is called 48 line = line where the enforce is called 49 50 Throws: 51 `this` upon any of conditions: 52 - !ok 53 - handle has error status 54 - handle has EOF status 55 - errno != 0 56 57 **************************************************************************/ 58 59 public void enforce ( bool ok, cstring filename, FILE* handle, 60 istring file = __FILE__, int line = __LINE__ ) 61 { 62 int err_num = handle ? ferror(handle) : .errno; 63 64 if (err_num) 65 { 66 if (handle) 67 clearerr(handle); 68 69 if (ok) 70 { 71 throw this.set(err_num, "", file, line) 72 .append(" (operation on '") 73 .append(filename) 74 .append("' returned success status, but errno is non-zero)"); 75 } 76 else 77 { 78 throw this.set(err_num, "", file, line) 79 .append(" (failed operation on '") 80 .append(filename) 81 .append("')"); 82 } 83 } 84 85 if ( !ok ) 86 { 87 if ( handle !is null && feof(handle) != 0 ) 88 { 89 // not really an errno 90 throw this.ReusableImpl 91 .set("Reading past end of file", file, line) 92 .append(" (failed operation on '") 93 .append(filename) 94 .append("')"); 95 } 96 else 97 { 98 throw this.ReusableImpl 99 .set("File operation failed, without an error", file, line) 100 .append(" (failed operation on '") 101 .append(filename) 102 .append("')"); 103 } 104 } 105 } 106 } 107 108 version (unittest) 109 { 110 import core.sys.posix.stdio; 111 import ocean.core.Test; 112 } 113 114 /// 115 unittest 116 { 117 auto e = new FileException; 118 auto f = fdopen(-1, "r".ptr); 119 size_t check_line; 120 121 try 122 { 123 check_line = __LINE__ + 1; 124 e.enforce(f !is null, "<42>", f); 125 assert (false); 126 } 127 catch (FileException e) 128 { 129 test!("==")( 130 e.message(), 131 "Bad file descriptor (failed operation on '<42>')"[] 132 ); 133 test!("==")(e.line, check_line); 134 } 135 }