ARTS  2.4.0(git:4fb77825)
file.cc
Go to the documentation of this file.
1 /* Copyright (C) 2000-2012 Stefan Buehler <sbuehler@ltu.se>
2 
3  This program is free software; you can redistribute it and/or modify it
4  under the terms of the GNU General Public License as published by the
5  Free Software Foundation; either version 2, or (at your option) any
6  later version.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16  USA. */
17 
19 // File description
21 
30 // External declarations
33 
34 #include "arts.h"
35 
36 #include <algorithm>
37 #include <cfloat>
38 #include <cmath>
39 #include <cstdio>
40 #include <cstdlib>
41 #include <limits>
42 #include <stdexcept>
43 
44 // For getdir
45 #include <dirent.h>
46 #include <errno.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 #include "array.h"
55 #include "file.h"
56 #include "parameters.h"
57 
59 // Default file names
61 
73 void filename_ascii(String& filename, const String& varname) {
74  if ("" == filename) {
75  extern const String out_basename;
76  filename = out_basename + "." + varname + ".aa";
77  }
78 }
79 
81 // Functions to open and read ASCII files
83 
93 void open_output_file(ofstream& file, const String& name) {
94  String ename = add_basedir(name);
95 
96  try {
97  // Tell the stream that it should throw exceptions.
98  // Badbit means that the entire stream is corrupted, failbit means
99  // that the last operation has failed, but the stream is still
100  // valid. We don't want either to happen!
101  // FIXME: This does not yet work in egcs-2.91.66, try again later.
102  file.exceptions(ios::badbit | ios::failbit);
103 
104  // c_str explicitly converts to c String.
105  file.open(ename.c_str());
106 
107  // See if the file is ok.
108  // FIXME: This should not be necessary anymore in the future, when
109  // g++ stream exceptions work properly. (In that case we would not
110  // get here if there really was a problem, because of the exception
111  // thrown by open().)
112  } catch (const std::exception& e) {
113  ostringstream os;
114  os << "Cannot open output file: " << ename << '\n'
115  << "Maybe you don't have write access "
116  << "to the directory or the file?";
117  throw runtime_error(os.str());
118  }
119 }
120 
127 #ifdef HAVE_REMOVE
128 void cleanup_output_file(ofstream& file, const String& name) {
129  if (file.is_open()) {
130  streampos fpos = file.tellp();
131  file.close();
132  if (!fpos) unlink(expand_path(name).c_str());
133  }
134 }
135 #else
136 void cleanup_output_file(ofstream&, const String&) {}
137 #endif
138 
147 void open_input_file(ifstream& file, const String& name) {
148  String ename = expand_path(name);
149 
150  // Command line parameters which give us the include search path.
151  extern const Parameters parameters;
153  allpaths.insert(
154  allpaths.end(), parameters.datapath.begin(), parameters.datapath.end());
155 
156  ArrayOfString matching_files;
157  find_file(matching_files, ename, allpaths);
158 
159  if (matching_files.nelem()) ename = matching_files[0];
160 
161  // Tell the stream that it should throw exceptions.
162  // Badbit means that the entire stream is corrupted.
163  // On the other hand, end of file will not lead to an exception, you
164  // have to check this manually!
165  file.exceptions(ios::badbit);
166 
167  // c_str explicitly converts to c String.
168  file.open(ename.c_str());
169 
170  // See if the file is ok.
171  // FIXME: This should not be necessary anymore in the future, when
172  // g++ stream exceptions work properly.
173  if (!file) {
174  ostringstream os;
175  os << "Cannot open input file: " << ename << '\n'
176  << "Maybe the file does not exist?";
177  throw runtime_error(os.str());
178  }
179 }
180 
190 void read_text_from_stream(ArrayOfString& text, istream& is) {
191  String linebuffer;
192 
193  // Read as long as `is' is good.
194  // Contary to what I understood from the book, the explicit check
195  // for eof is necessary here, otherwise the last line is read twice
196  // if it is not terminated by a newline character!
197  while (is && is.good() && !is.eof()) {
198  // Read line from file into linebuffer:
199  getline(is, linebuffer);
200 
201  // Append to end of text:
202  text.push_back(linebuffer);
203  }
204 
205  // Check for error:
206  // FIXME: This should not be necessary anymore when stream
207  // exceptions work properly.
208  if (!is.eof()) {
209  ostringstream os;
210  os << "Read Error. Last line read:\n" << linebuffer;
211  throw runtime_error(os.str());
212  }
213 }
214 
225 void read_text_from_file(ArrayOfString& text, const String& name) {
226  ifstream ifs;
227 
228  // Open input stream:
229  open_input_file(ifs, name);
230  // No need to check for error, because open_input_file throws a
231  // runtime_error with an appropriate error message.
232 
233  // Read the text from the stream. Here we catch the exception,
234  // because then we can issue a nicer error message that includes the
235  // filename.
236  try {
237  read_text_from_stream(text, ifs);
238  } catch (const std::runtime_error& x) {
239  ostringstream os;
240  os << "Error reading file: " << name << '\n' << x.what();
241  throw runtime_error(os.str());
242  }
243 }
244 
253 void replace_all(String& s, const String& what, const String& with) {
254  Index j = s.find(what);
255  while (j != s.npos) {
256  s.replace(j, 1, with);
257  j = s.find(what, j + with.size());
258  }
259 }
260 
271 int check_newline(const String& s) {
272  String d = s;
273  int result = 0;
274 
275  // Remove all whitespaces except \n
276  replace_all(d, " ", "");
277  replace_all(d, "\t", "");
278  replace_all(d, "\r", "");
279 
280  const char* cp = d.c_str();
281  while ((*cp == '\n') && *cp) cp++;
282 
283  if (!(*cp)) result = 1;
284 
285  if (!result && d[d.length() - 1] != '\n')
286  result = 2;
287  else if (!result && d.length() > 2 && d[d.length() - 1] == '\n' &&
288  d[d.length() - 2] == '\n')
289  result = 3;
290 
291  return result;
292 }
293 
303 bool file_exists(const String& filename) {
304  bool exists = false;
305 
306  struct stat st;
307  if (lstat(filename.c_str(), &st) >= 0 && !S_ISDIR(st.st_mode)) {
308  fstream fin;
309  fin.open(filename.c_str(), ios::in);
310  if (fin.is_open()) {
311  exists = true;
312  }
313  fin.close();
314  }
315 
316  return exists;
317 }
318 
328 String get_absolute_path(const String& filename) {
329  char* fullrealpath;
330  fullrealpath = realpath(filename.c_str(), NULL);
331  if (fullrealpath) {
332  String retpath(fullrealpath);
333  free(fullrealpath);
334  return retpath;
335  } else
336  return filename;
337 }
338 
353 bool find_file(ArrayOfString& matches,
354  const String& filename,
355  const ArrayOfString& paths,
356  const ArrayOfString& extensions) {
357  bool exists = false;
358  String efilename = expand_path(filename);
359 
360  // filename contains full path
361  if (!paths.nelem() || (efilename.nelem() && efilename[0] == '/')) {
362  for (ArrayOfString::const_iterator ext = extensions.begin();
363  ext != extensions.end();
364  ext++) {
365  String fullpath = get_absolute_path(efilename + *ext);
366  // Full path + extension
367  if (file_exists(fullpath)) {
368  if (std::find(matches.begin(), matches.end(), fullpath) ==
369  matches.end())
370  matches.push_back(fullpath);
371  exists = true;
372  }
373  }
374  }
375  // filename contains no or relative path
376  else {
377  for (ArrayOfString::const_iterator path = paths.begin();
378  path != paths.end();
379  path++) {
380  for (ArrayOfString::const_iterator ext = extensions.begin();
381  ext != extensions.end();
382  ext++) {
383  String fullpath =
384  get_absolute_path(expand_path(*path) + "/" + efilename + *ext);
385 
386  if (file_exists(fullpath)) {
387  if (std::find(matches.begin(), matches.end(), fullpath) ==
388  matches.end())
389  matches.push_back(fullpath);
390  exists = true;
391  }
392  }
393  }
394  }
395 
396  return exists;
397 }
398 
414 void find_xml_file(String& filename, const Verbosity& verbosity) {
415  // Command line parameters which give us the include search path.
416  extern const Parameters parameters;
418  allpaths.insert(
419  allpaths.end(), parameters.datapath.begin(), parameters.datapath.end());
420 
421  ArrayOfString matching_files;
422  find_file(matching_files, filename, allpaths, {"", ".xml", ".gz", ".xml.gz"});
423 
424  if (matching_files.nelem() > 1) {
425  CREATE_OUT1;
426  out1
427  << " WARNING: More than one file matching this name exists in the data path.\n"
428  << " Using the first file (1) found:\n";
429  for (Index i = 0; i < matching_files.nelem(); i++)
430  out1 << " (" << i + 1 << ") " << matching_files[i] << "\n";
431  } else if (!matching_files.nelem()) {
432  ostringstream os;
433  os << "Cannot find input file: " << filename << endl;
434  os << "Search path: " << allpaths << endl;
435  throw runtime_error(os.str());
436  }
437 
438  filename = matching_files[0];
439 }
440 
453  // Command line parameters which give us the include search path.
454  extern const Parameters parameters;
456  allpaths.insert(
457  allpaths.end(), parameters.datapath.begin(), parameters.datapath.end());
458 
459  ArrayOfString matching_files;
460  find_file(matching_files, filename, allpaths, {"", ".xml", ".gz", ".xml.gz"});
461 
462  if (matching_files.nelem()) {
463  filename = matching_files[0];
464  return true;
465  } else {
466  return false;
467  }
468 }
469 
480 String expand_path(const String& path) {
481  if ((path.nelem() == 1 && path[0] == '~') ||
482  (path.nelem() > 1 && path[0] == '~' && path[1] == '/')) {
483  return String(getenv("HOME")) + String(path, 1);
484  } else {
485  return path;
486  }
487 }
488 
499 String add_basedir(const String& path) {
500  extern const Parameters parameters;
501  String expanded_path = expand_path(path);
502 
503  if (parameters.outdir.nelem() && path.nelem() && path[0] != '/') {
504  expanded_path = parameters.outdir + '/' + expanded_path;
505  }
506 
507  return expanded_path;
508 }
509 
511 
519 void get_dirname(String& dirname, const String& path) {
520  dirname = "";
521  if (!path.nelem()) return;
522 
523  ArrayOfString fileparts;
524  path.split(fileparts, "/");
525  if (path[0] == '/') dirname = "/";
526  if (fileparts.nelem() > 1) {
527  for (Index i = 0; i < fileparts.nelem() - 1; i++) {
528  dirname += fileparts[i];
529  if (i < fileparts.nelem() - 2) dirname += "/";
530  }
531  }
532 }
533 
535 
543 void list_directory(ArrayOfString& files, String dirname) {
544  DIR* dp;
545  struct dirent* dirp;
546  if ((dp = opendir(dirname.c_str())) == NULL) {
547  ostringstream os;
548  os << "Error(" << errno << ") opening " << dirname << endl;
549  throw runtime_error(os.str());
550  }
551 
552  while ((dirp = readdir(dp)) != NULL) {
553  files.push_back(String(dirp->d_name));
554  }
555 
556  closedir(dp);
557 }
558 
571 void make_filename_unique(String& filename, const String& extension) {
572  String basename = filename;
573  String extensionname;
574 
575  // Split filename into base and extension
576  if (extension.length()) {
577  size_t pos = filename.rfind(extension);
578  if (pos == filename.length() - extension.length()) {
579  basename = filename.substr(0, filename.length() - extension.length());
580  extensionname = extension;
581  }
582  }
583 
584  Index filenumber = 0;
585  ostringstream newfilename;
586  newfilename << basename << extensionname;
587 
588  while (file_exists(newfilename.str()) ||
589  file_exists(newfilename.str() + ".gz")) {
590  filenumber++;
591  newfilename.str("");
592  newfilename << basename << "." << filenumber << extensionname;
593  }
594 
595  filename = newfilename.str();
596 }
597 
599 // IO manipulation classes for parsing nan and inf
601 
603  const char* exp[] = {"", "inf", "Inf", "nan", "NaN"};
604  const char* e = exp[0];
605  int l = 0;
606  char inf[4] = "\0\0\0";
607  char* c = inf;
608  if (neg) *c++ = '-';
609  in.clear();
610  if (!(in >> *c).good()) return *this;
611  switch (*c) {
612  case 'i':
613  e = exp[l = 1];
614  break;
615  case 'I':
616  e = exp[l = 2];
617  break;
618  case 'n':
619  e = exp[l = 3];
620  break;
621  case 'N':
622  e = exp[l = 4];
623  break;
624  }
625  while (*c == *e) {
626  if ((e - exp[l]) == 2) break;
627  ++e;
628  if (!(in >> *++c).good()) break;
629  }
630  if (in.good() && *c == *e) {
631  switch (l) {
632  case 1:
633  case 2:
634  x = std::numeric_limits<double>::infinity();
635  break;
636  case 3:
637  case 4:
638  x = std::numeric_limits<double>::quiet_NaN();
639  break;
640  }
641  if (neg) x = -x;
642  return *this;
643  } else if (!in.good()) {
644  if (!in.fail()) return *this;
645  in.clear();
646  --c;
647  }
648  do {
649  in.putback(*c);
650  } while (c-- != inf);
651  in.setstate(std::ios_base::failbit);
652  return *this;
653 }
654 
655 const double_imanip& operator>>(std::istream& in, const double_imanip& dm) {
656  dm.in = &in;
657  return dm;
658 }
Parameters::outdir
String outdir
If this is specified (with the -o –outdir option), it is used as the base directory for the report fi...
Definition: parameters.h:87
out_basename
String out_basename
The basename for the report file and for all other output files.
Definition: messages.cc:42
Parameters
Structure to hold all command line Parameters.
Definition: parameters.h:42
ARTS::Var::verbosity
Verbosity verbosity(Workspace &ws) noexcept
Definition: autoarts.h:7112
cleanup_output_file
void cleanup_output_file(ofstream &, const String &)
Closes the file.
Definition: file.cc:136
open_input_file
void open_input_file(ifstream &file, const String &name)
Open a file for reading.
Definition: file.cc:147
get_dirname
void get_dirname(String &dirname, const String &path)
Return the parent directory of a path.
Definition: file.cc:519
parameters
Parameters parameters
Holds the command line parameters.
Definition: parameters.cc:41
array.h
This file contains the definition of Array.
CREATE_OUT1
#define CREATE_OUT1
Definition: messages.h:205
operator>>
const double_imanip & operator>>(std::istream &in, const double_imanip &dm)
Definition: file.cc:655
Array< String >
read_text_from_stream
void read_text_from_stream(ArrayOfString &text, istream &is)
Read an ASCII stream and append the contents to the String array text.
Definition: file.cc:190
my_basic_string< char >
get_absolute_path
String get_absolute_path(const String &filename)
Convert relative path to absolute path.
Definition: file.cc:328
double_imanip
Input manipulator class for doubles to enable nan and inf parsing.
Definition: file.h:117
find_xml_file
void find_xml_file(String &filename, const Verbosity &verbosity)
Find an xml file.
Definition: file.cc:414
Verbosity
Definition: messages.h:49
double_istream::in
std::istream & in
Definition: file.h:113
make_filename_unique
void make_filename_unique(String &filename, const String &extension)
Make filename unique.
Definition: file.cc:571
file_exists
bool file_exists(const String &filename)
Checks if the given file exists.
Definition: file.cc:303
parameters.h
This file contains header information for the dealing with command line parameters.
double_imanip::in
std::istream * in
Definition: file.h:129
open_output_file
void open_output_file(ofstream &file, const String &name)
Open a file for writing.
Definition: file.cc:93
my_basic_string::nelem
Index nelem() const
Number of elements.
Definition: mystring.h:246
list_directory
void list_directory(ArrayOfString &files, String dirname)
Return list of files in directory.
Definition: file.cc:543
my_basic_string::split
void split(Array< my_basic_string< charT > > &aos, const my_basic_string< charT > &delim) const
Split string into substrings.
Definition: mystring.h:206
read_text_from_file
void read_text_from_file(ArrayOfString &text, const String &name)
Reads an ASCII file and appends the contents to the String vector text.
Definition: file.cc:225
expand_path
String expand_path(const String &path)
Definition: file.cc:480
String
my_basic_string< char > String
The String type for ARTS.
Definition: mystring.h:280
check_newline
int check_newline(const String &s)
Checks if there is exactly one newline character at the end of the string.
Definition: file.cc:271
filename_ascii
void filename_ascii(String &filename, const String &varname)
Gives the default file name for the ASCII formats.
Definition: file.cc:73
double_istream
Input stream class for doubles that correctly handles nan and inf.
Definition: file.h:93
add_basedir
String add_basedir(const String &path)
Definition: file.cc:499
find_xml_file_existence
bool find_xml_file_existence(String &filename)
As find_xml_file but does not throw in the main body.
Definition: file.cc:452
file.h
This file contains basic functions to handle ASCII files.
double_istream::parse_on_fail
double_istream & parse_on_fail(double &x, bool neg)
Definition: file.cc:602
ARTS::Var::x
Vector x(Workspace &ws) noexcept
Definition: autoarts.h:7346
Index
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
my_basic_string::npos
static const Index npos
Define npos:
Definition: mystring.h:106
Parameters::includepath
ArrayOfString includepath
List of paths to search for include files.
Definition: parameters.h:106
Parameters::datapath
ArrayOfString datapath
List of paths to search for data files.
Definition: parameters.h:108
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:195
arts.h
The global header file for ARTS.
find_file
bool find_file(ArrayOfString &matches, const String &filename, const ArrayOfString &paths, const ArrayOfString &extensions)
Searches through paths for a file with a matching name.
Definition: file.cc:353
replace_all
void replace_all(String &s, const String &what, const String &with)
Replace all occurances of ‘what’ in ‘s’ with ‘with’.
Definition: file.cc:253