Vcsn  2.8
Be Rational
stream.cc
Go to the documentation of this file.
1 #include <vcsn/misc/stream.hh>
2 
3 #include <cassert>
4 #include <cstring> // strerror
5 #include <istream>
6 #include <fstream>
7 
8 #include <vcsn/misc/raise.hh>
9 
10 namespace vcsn
11 {
12 
13  std::ostream cnull{nullptr};
14  std::wostream wcnull{nullptr};
15 
16  std::string
17  bracketed(std::istream& i, const char lbracket, const char rbracket)
18  {
19  assert(i.peek() == lbracket);
20  i.ignore();
21  size_t level = 1;
22  auto&& o = std::ostringstream{};
23  int c;
24  while ((c = i.get()) != -1)
25  {
26  if (c == lbracket)
27  ++level;
28  else if (c == rbracket
29  && !--level)
30  return o.str();
31  o << char(c);
32  }
33  raise("missing ", str_quote(rbracket), " after ",
34  str_quote(lbracket + o.str()));
35  }
36 
37 
38  bool
39  equal_files(const std::string& fn1, const std::string& fn2)
40  {
41  auto&& f1 = std::ifstream(fn1, std::ifstream::binary|std::ifstream::ate);
42  auto&& f2 = std::ifstream(fn2, std::ifstream::binary|std::ifstream::ate);
43 
44  if (f1.fail() || f2.fail())
45  return false;
46 
47  if (f1.tellg() != f2.tellg())
48  return false;
49 
50  f1.seekg(0, std::ifstream::beg);
51  f2.seekg(0, std::ifstream::beg);
52  return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
53  std::istreambuf_iterator<char>(),
54  std::istreambuf_iterator<char>(f2.rdbuf()));
55  }
56 
57 
58  std::string expand_tilda(const std::string& s)
59  {
60  auto res = s;
61  if (!res.empty() && res[0] == '~')
62  {
63  assert(res.size() == 1 || res[1] == '/');
64  auto home = xgetenv("HOME", xgetenv("USERPROFILE"));
65  char const *hdrive = getenv("HOMEDRIVE");
66  char const *hres = getenv("HOMERES");
67  if (!home.empty())
68  res.replace(0, 1, home);
69  else if (hdrive && hres)
70  res.replace(0, 1, std::string(hdrive) + hres);
71  else
72  res.replace(0, 1, xgetenv("VCSN_TMPDIR", "/tmp"));
73  }
74  return res;
75  }
76 
77  namespace
78  {
80  std::string
81  get_line(std::istream& is)
82  {
83  auto res = std::string{};
84  std::getline(is, res, '\n');
85  if (!is.good())
86  // This shouldn't happen; however it's best to fail cleanly.
87  is.clear();
88  return res;
89  }
90  }
91 
92  char get_char(std::istream& i)
93  {
94  int res = i.get();
95  if (res == '\\')
96  switch (int c = i.get())
97  {
98  // Usual escapes.
99 #define CASE(Key, Value) \
100  case Key: res = Value; i.ignore(); break
101  CASE('a', '\a');
102  CASE('b', '\b');
103  CASE('f', '\f');
104  CASE('n', '\n');
105  CASE('r', '\r');
106  CASE('t', '\t');
107  CASE('v', '\v');
108 #undef CASE
109 
110  // Hexadecimal escapes.
111  case 'x':
112  {
113  // Handle hexadecimal escape.
114  int c1 = i.get();
115  require(c1 != EOF,
116  "get_char: unexpected end-of-file"
117  " after: \\x");
118  require(isxdigit(c1),
119  "get_char: invalid escape: \\x", char(c1));
120  int c2 = i.get();
121  require(c2 != EOF,
122  "get_char: unexpected end-of-file"
123  " after: \\x", char(c1));
124  require(isxdigit(c2),
125  "get_char: invalid escape: \\x",
126  char(c1), char(c2));
127  res = std::stoi(std::string{char(c1), char(c2)}, nullptr, 16);
128  }
129  break;
130 
131  // Other escapes, e.g., \\, \", \', etc. \(, \) and \-
132  // are used in setalpha::make, e.g., char_letters(\(\-\)).
133  default:
134  VCSN_REQUIRE(!std::isalnum(c),
135  "get_char: invalid escape: ",
136  str_quote('\\', char(c)),
137  " in ",
138  str_quote('\\', char(c), get_line(i)));
139  res = c;
140  break;
141  }
142  require(res != EOF,
143  "get_char: unexpected end-of-file");
144  return res;
145  }
146 
147  char eat(std::istream& is, char c)
148  {
149  if (is.peek() == c)
150  {
151  is.ignore();
152  return c;
153  }
154  else if (is.peek() == EOF)
155  fail_reading(is, "unexpected end-of-file: expected ", str_quote(c));
156  else
157  fail_reading(is, "expected ", str_quote(c), ", got");
158  }
159 
160  const std::string& eat(std::istream& is, const std::string& expect)
161  {
162  std::string s;
163  char c;
164  size_t cnt = expect.size();
165  while (cnt && is >> c)
166  {
167  s.append(1, c);
168  --cnt;
169  }
170  VCSN_REQUIRE(s == expect,
171  "unexpected: ", str_quote(s),
172  ": expected ", str_quote(expect));
173  return expect;
174  }
175 
176  // http://stackoverflow.com/questions/2602013.
177  std::string
178  get_file_contents(const std::string& file)
179  {
180  auto&& in = std::ifstream(file.c_str(), std::ios::in | std::ios::binary);
181  VCSN_REQUIRE(in.good(), "cannot read file: ", file, ": ", strerror(errno));
182 
183  std::string res;
184  in.seekg(0, std::ios::end);
185  res.resize(in.tellg());
186  in.seekg(0, std::ios::beg);
187  in.read(&res[0], res.size());
188  in.close();
189  return res;
190  }
191 
192  std::shared_ptr<std::istream>
193  open_input_file(const std::string& file)
194  {
195  auto res = std::shared_ptr<std::istream>{};
196  if (file.empty() || file == "-")
197  res.reset(&std::cin, [](...){});
198  else
199  {
200  res.reset(new std::ifstream(file.c_str()));
201  VCSN_REQUIRE(res->good(),
202  "cannot open ", file, " for reading: ", strerror(errno));
203  }
204  return res;
205  }
206 
207  std::shared_ptr<std::ostream>
208  open_output_file(const std::string& file)
209  {
210  auto res = std::shared_ptr<std::ostream>{};
211  if (file.empty() || file == "-")
212  res.reset(&std::cout, [](...){});
213  else
214  {
215  res.reset(new std::ofstream(file.c_str()));
216  VCSN_REQUIRE(res->good(),
217  "cannot open ", file, " for writing: ", strerror(errno));
218  }
219  return res;
220  }
221 
222  void skip_space(std::istream& is)
223  {
224  while (isspace(is.peek()))
225  is.ignore();
226  }
227 
228  std::string
229  xgetenv(const std::string& var, const std::string& val)
230  {
231  const char* cp = getenv(var.c_str());
232  return cp ? cp : val;
233  }
234 }
std::string xgetenv(const std::string &var, const std::string &val="")
getenv(var) if defined, otherwise val.
Definition: stream.cc:229
char get_char(std::istream &i)
Read a single char, with possible -escape support.
Definition: stream.cc:92
char eat(std::istream &is, char c)
Check lookahead character and advance.
Definition: stream.cc:147
void skip_space(std::istream &is)
Ignore spaces.
Definition: stream.cc:222
std::string get_file_contents(const std::string &file)
Return the contents of file.
Definition: stream.cc:178
ATTRIBUTE_NORETURN void fail_reading(std::istream &is, Args &&... args)
Throw an exception after failing to read from is.
Definition: stream.hh:76
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
std::string expand_tilda(const std::string &res)
Expand initial "~" in res.
Definition: stream.cc:58
bool equal_files(const std::string &fn1, const std::string &fn2)
Whether two files have exactly equal contents.
Definition: stream.cc:39
std::ostream cnull
An narrow-char stream that discards the output.
Definition: stream.cc:13
Definition: a-star.hh:8
std::shared_ptr< std::istream > open_input_file(const std::string &file)
Open file for reading and return its autoclosing stream.
Definition: stream.cc:193
auto in(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions arriving to state s.
Definition: automaton.hh:135
std::shared_ptr< std::ostream > open_output_file(const std::string &file)
Open file for writing and return its autoclosing stream.
Definition: stream.cc:208
std::string bracketed(std::istream &i, char lbracket, char rbracket)
Extract the string which is here between lbracket and rbracket.
Definition: stream.cc:17
std::string str_quote(Args &&... args)
Convert to a string, in quotes.
Definition: escape.hh:49
#define CASE(Key, Value)
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:87
std::wostream wcnull
An wide-char stream that discards the output.
Definition: stream.cc:14
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:98
return res
Definition: multiply.hh:399