More error checking to gmx::File.
[alexxy/gromacs.git] / src / gromacs / utility / file.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements gmx::File.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_utility
37  */
38 #include "file.h"
39
40 #include <cerrno>
41 #include <cstdio>
42
43 #include <string>
44 #include <vector>
45
46 #include "gromacs/utility/exceptions.h"
47 #include "gromacs/utility/gmxassert.h"
48 #include "gromacs/utility/format.h"
49
50 namespace gmx
51 {
52
53 File::File(const char *filename, const char *mode)
54     : fp_(NULL)
55 {
56     open(filename, mode);
57 }
58
59 File::File(const std::string &filename, const char *mode)
60     : fp_(NULL)
61 {
62     open(filename, mode);
63 }
64
65 File::~File()
66 {
67     if (fp_ != NULL)
68     {
69         if (fclose(fp_) != 0)
70         {
71             // TODO: Log the error somewhere
72         }
73     }
74 }
75
76 void File::open(const char *filename, const char *mode)
77 {
78     GMX_RELEASE_ASSERT(fp_ == NULL,
79                        "Attempted to open the same file object twice");
80     // TODO: Port all necessary functionality from ffopen() here.
81     fp_ = fopen(filename, mode);
82     if (fp_ == NULL)
83     {
84         GMX_THROW_WITH_ERRNO(
85                 FileIOError(formatString("Could not open file '%s'", filename)),
86                 "fopen", errno);
87     }
88 }
89
90 void File::open(const std::string &filename, const char *mode)
91 {
92     open(filename.c_str(), mode);
93 }
94
95 void File::close()
96 {
97     GMX_RELEASE_ASSERT(fp_ != NULL,
98                        "Attempted to close a file object that is not open");
99     bool bOk = (fclose(fp_) == 0);
100     fp_ = NULL;
101     if (!bOk)
102     {
103         GMX_THROW_WITH_ERRNO(
104                 FileIOError("Error while closing file"), "fclose", errno);
105     }
106 }
107
108 FILE *File::handle()
109 {
110     GMX_RELEASE_ASSERT(fp_ != NULL,
111                        "Attempted to access a file object that is not open");
112     return fp_;
113 }
114
115 void File::readBytes(void *buffer, size_t bytes)
116 {
117     GMX_RELEASE_ASSERT(fp_ != NULL,
118                        "Attempted to access a file object that is not open");
119     errno = 0;
120     // TODO: Retry based on errno or something else?
121     size_t bytesRead = std::fread(buffer, 1, bytes, fp_);
122     if (bytesRead != bytes)
123     {
124         if (feof(fp_))
125         {
126             GMX_THROW(FileIOError(
127                         formatString("Premature end of file\n"
128                                      "Attempted to read: %d bytes\n"
129                                      "Successfully read: %d bytes",
130                                      static_cast<int>(bytes),
131                                      static_cast<int>(bytesRead))));
132         }
133         else
134         {
135             GMX_THROW_WITH_ERRNO(FileIOError("Error while reading file"),
136                                  "fread", errno);
137         }
138     }
139 }
140
141 // static
142 std::string File::readToString(const char *filename)
143 {
144     File file(filename, "r");
145     FILE *fp = file.handle();
146
147     if (std::fseek(fp, 0L, SEEK_END) != 0)
148     {
149         GMX_THROW_WITH_ERRNO(FileIOError("Seeking to end of file failed"),
150                              "fseek", errno);
151     }
152     long len = std::ftell(fp);
153     if (len == -1)
154     {
155         GMX_THROW_WITH_ERRNO(FileIOError("Reading file length failed"),
156                              "ftell", errno);
157     }
158     if (std::fseek(fp, 0L, SEEK_SET) != 0)
159     {
160         GMX_THROW_WITH_ERRNO(FileIOError("Seeking to start of file failed"),
161                              "fseek", errno);
162     }
163
164     std::vector<char> data(len);
165     file.readBytes(&data[0], len);
166     std::string result(&data[0], len);
167
168     file.close();
169     return result;
170 }
171
172 // static
173 std::string File::readToString(const std::string &filename)
174 {
175     return readToString(filename.c_str());
176 }
177
178 } // namespace gmx