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 }