1 #ifndef IOUTIL_H
2 #define IOUTIL_H 1
3 
4 #include <cassert>
5 #include <cerrno>
6 #include <cstdlib>
7 #include <cstring> // for strerror
8 #include <fstream>
9 #include <iostream>
10 #include <limits> // for numeric_limits
11 #include <string>
12 
13 /** Print an error message and exit if stream is not good. */
assert_good(const std::ios & stream,const std::string & path)14 static inline void assert_good(const std::ios& stream,
15 		const std::string& path)
16 {
17 	if (!stream.good()) {
18 		std::cerr << "error: `" << path << "': "
19 			<< strerror(errno) << std::endl;
20 		exit(EXIT_FAILURE);
21 	}
22 }
23 
24 /** Print an error message and exit if stream is not eof. */
assert_eof(std::istream & in,const std::string & path)25 static inline void assert_eof(std::istream& in,
26 		const std::string& path)
27 {
28 	if (!in.eof()) {
29 		in.clear();
30 		std::string s;
31 		std::getline(in, s);
32 		std::cerr << "error: `" << path << "': "
33 			"Expected end-of-file and saw `" << s << "'\n";
34 		exit(EXIT_FAILURE);
35 	}
36 }
37 
38 /** This input stream manipulator skips the specified string. */
39 struct expect {
40 	const char* s;
expectexpect41 	expect(const char* s) : s(s) { }
42 };
43 
44 static inline std::istream& operator>>(std::istream& in, expect o)
45 {
46 	for (const char* p = o.s; *p != '\0'; ++p) {
47 		if (*p == ' ') {
48 			in >> std::ws;
49 		} else {
50 			char c = in.get();
51 			if (!in || c != *p) {
52 				std::cerr << "error: Expected `" << p
53 					<< "' and saw ";
54 				if (in) {
55 					std::cerr << '`' << c << "'\n";
56 					std::string s;
57 					if (getline(in, s) && !s.empty())
58 						std::cerr << "near: " << c << s << '\n';
59 				} else if (in.eof())
60 					std::cerr << "end-of-file\n";
61 				else
62 					std::cerr << "I/O error\n";
63 				exit(EXIT_FAILURE);
64 			}
65 		}
66 	}
67 	return in;
68 }
69 
70 /** This input stream manipulator discards characters until reaching
71  * the delimeter. */
72 struct Ignore {
73 	const char delim;
74 	size_t n;
75 	Ignore(const char delim,
76 			size_t n = std::numeric_limits<std::streamsize>::max())
delimIgnore77 		: delim(delim), n(n) { }
78 };
79 
80 static inline std::istream& operator>>(std::istream& in, Ignore o)
81 {
82 	return in.ignore(o.n, o.delim);
83 }
84 
85 /** Skip the specified character if it's next in the input stream. */
86 struct Skip {
87 	char c;
SkipSkip88 	Skip(const char c) : c(c) { }
89 };
90 
91 static inline std::istream& operator>>(std::istream& in, Skip o)
92 {
93 	if (in.peek() == o.c)
94 		in.ignore(1);
95 	return in;
96 }
97 
98 /** Read a file and store it in the specified vector. */
99 template <typename Vector>
readFile(const char * path,Vector & s)100 static inline void readFile(const char* path, Vector& s)
101 {
102 	std::ifstream in(path);
103 	assert_good(in, path);
104 	in.seekg(0, std::ios::end);
105 	ssize_t n = in.tellg();
106 	assert(n > 0);
107 	s.resize(n);
108 	in.seekg(0, std::ios::beg);
109 	assert_good(in, path);
110 	char *p = reinterpret_cast<char*>(&s[0]);
111 #if __MACH__
112 	// Read 1 GB at a time. Reads of 2 GB or more fail.
113 	const ssize_t N = 1024 * 1024 * 1024;
114 	for (; n > N; n -= N, p += N) {
115 		in.read(p, N);
116 		assert_good(in, path);
117 		assert(in.gcount() == N);
118 	}
119 #endif
120 	in.read(p, n);
121 	assert_good(in, path);
122 	assert(in.gcount() == n);
123 }
124 
125 /** Copy a file */
copyFile(const std::string & srcPath,const std::string & dstPath)126 inline static void copyFile(const std::string& srcPath,
127 	const std::string& dstPath)
128 {
129 	assert(srcPath != dstPath);
130 	std::ifstream src(srcPath.c_str(), std::ios::binary);
131 	std::ofstream dst(dstPath.c_str(), std::ios::binary);
132 	dst << src.rdbuf();
133 	assert(dst);
134 }
135 
136 #endif
137