1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2010 - 2015, Göteborg Bit Factory.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 #include <cmake.h>
28 #include <fstream>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 #include <File.h>
36 #include <text.h>
37 
38 ////////////////////////////////////////////////////////////////////////////////
File()39 File::File ()
40 : Path::Path ()
41 , _fh (NULL)
42 , _h (-1)
43 , _locked (false)
44 {
45 }
46 
47 ////////////////////////////////////////////////////////////////////////////////
File(const Path & other)48 File::File (const Path& other)
49 : Path::Path (other)
50 , _fh (NULL)
51 , _h (-1)
52 , _locked (false)
53 {
54 }
55 
56 ////////////////////////////////////////////////////////////////////////////////
File(const File & other)57 File::File (const File& other)
58 : Path::Path (other)
59 , _fh (NULL)
60 , _h (-1)
61 , _locked (false)
62 {
63 }
64 
65 ////////////////////////////////////////////////////////////////////////////////
File(const std::string & in)66 File::File (const std::string& in)
67 : Path::Path (in)
68 , _fh (NULL)
69 , _h (-1)
70 , _locked (false)
71 {
72 }
73 
74 ////////////////////////////////////////////////////////////////////////////////
~File()75 File::~File ()
76 {
77   if (_fh)
78     close ();
79 }
80 
81 ////////////////////////////////////////////////////////////////////////////////
operator =(const File & other)82 File& File::operator= (const File& other)
83 {
84   if (this != &other)
85     Path::operator= (other);
86 
87   _locked = false;
88   return *this;
89 }
90 
91 ////////////////////////////////////////////////////////////////////////////////
create(int mode)92 bool File::create (int mode /* = 0640 */)
93 {
94   if (open ())
95   {
96     fchmod (_h, mode);
97     close ();
98     return true;
99   }
100 
101   return false;
102 }
103 
104 ////////////////////////////////////////////////////////////////////////////////
remove() const105 bool File::remove () const
106 {
107   return unlink (_data.c_str ()) == 0 ? true : false;
108 }
109 
110 ////////////////////////////////////////////////////////////////////////////////
open()111 bool File::open ()
112 {
113   if (_data != "")
114   {
115     if (! _fh)
116     {
117       bool already_exists = exists ();
118       if (already_exists)
119         if (!readable () || !writable ())
120           throw std::string (format ("ERROR: Task does not have the correct permissions for '{1}'.", _data));
121 
122       _fh = fopen (_data.c_str (), (already_exists ? "r+" : "w+"));
123       if (_fh)
124       {
125         _h = fileno (_fh);
126         _locked = false;
127         return true;
128       }
129     }
130     else
131       return true;
132   }
133 
134   return false;
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
openAndLock()138 bool File::openAndLock ()
139 {
140   return open () && lock ();
141 }
142 
143 ////////////////////////////////////////////////////////////////////////////////
close()144 void File::close ()
145 {
146   if (_fh)
147   {
148     if (_locked)
149       unlock ();
150 
151     fclose (_fh);
152     _fh = NULL;
153     _h = -1;
154     _locked = false;
155   }
156 }
157 
158 ////////////////////////////////////////////////////////////////////////////////
lock()159 bool File::lock ()
160 {
161   _locked = false;
162   if (_fh && _h != -1)
163   {
164                     // l_type   l_whence  l_start  l_len  l_pid
165     struct flock fl = {F_WRLCK, SEEK_SET, 0,       0,     0 };
166     fl.l_pid = getpid ();
167     if (fcntl (_h, F_SETLKW, &fl) == 0)
168       _locked = true;
169   }
170 
171   return _locked;
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
unlock()175 void File::unlock ()
176 {
177   if (_locked)
178   {
179                     // l_type   l_whence  l_start  l_len  l_pid
180     struct flock fl = {F_UNLCK, SEEK_SET, 0,       0,     0 };
181     fl.l_pid = getpid ();
182 
183     fcntl (_h, F_SETLK, &fl);
184     _locked = false;
185     }
186 }
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 // Opens if necessary.
read(std::string & contents)190 void File::read (std::string& contents)
191 {
192   contents = "";
193   contents.reserve (size ());
194 
195   std::ifstream in (_data.c_str ());
196   if (in.good ())
197   {
198     std::string line;
199     line.reserve (512 * 1024);
200     while (getline (in, line))
201       contents += line + "\n";
202 
203     in.close ();
204   }
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 // Opens if necessary.
read(std::vector<std::string> & contents)209 void File::read (std::vector <std::string>& contents)
210 {
211   contents.clear ();
212 
213   std::ifstream in (_data.c_str ());
214   if (in.good ())
215   {
216     std::string line;
217     line.reserve (512 * 1024);
218     while (getline (in, line))
219       contents.push_back (line);
220 
221     in.close ();
222   }
223 }
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 // Opens if necessary.
write(const std::string & line)227 void File::write (const std::string& line)
228 {
229   if (!_fh)
230     open ();
231 
232   if (_fh)
233     fputs (line.c_str (), _fh);
234 }
235 
236 ////////////////////////////////////////////////////////////////////////////////
237 // Opens if necessary.
write(const std::vector<std::string> & lines)238 void File::write (const std::vector <std::string>& lines)
239 {
240   if (!_fh)
241     open ();
242 
243   if (_fh)
244   {
245     std::vector <std::string>::const_iterator it;
246     for (it = lines.begin (); it != lines.end (); ++it)
247       fputs (it->c_str (), _fh);
248   }
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 // Opens if necessary.
append(const std::string & line)253 void File::append (const std::string& line)
254 {
255   if (!_fh)
256     open ();
257 
258   if (_fh)
259   {
260     fseek (_fh, 0, SEEK_END);
261     fputs (line.c_str (), _fh);
262   }
263 }
264 
265 ////////////////////////////////////////////////////////////////////////////////
266 // Opens if necessary.
append(const std::vector<std::string> & lines)267 void File::append (const std::vector <std::string>& lines)
268 {
269   if (!_fh)
270     open ();
271 
272   if (_fh)
273   {
274     fseek (_fh, 0, SEEK_END);
275     std::vector <std::string>::const_iterator it;
276     for (it = lines.begin (); it != lines.end (); ++it)
277       fputs (((*it) + "\n").c_str (), _fh);
278   }
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
truncate()282 void File::truncate ()
283 {
284   if (!_fh)
285     open ();
286 
287   if (_fh)
288     ftruncate (_h, 0);
289 }
290 
291 ////////////////////////////////////////////////////////////////////////////////
292 //  S_IFMT          0170000  type of file
293 //         S_IFIFO  0010000  named pipe (fifo)
294 //         S_IFCHR  0020000  character special
295 //         S_IFDIR  0040000  directory
296 //         S_IFBLK  0060000  block special
297 //         S_IFREG  0100000  regular
298 //         S_IFLNK  0120000  symbolic link
299 //         S_IFSOCK 0140000  socket
300 //         S_IFWHT  0160000  whiteout
301 //  S_ISUID         0004000  set user id on execution
302 //  S_ISGID         0002000  set group id on execution
303 //  S_ISVTX         0001000  save swapped text even after use
304 //  S_IRUSR         0000400  read permission, owner
305 //  S_IWUSR         0000200  write permission, owner
306 //  S_IXUSR         0000100  execute/search permission, owner
mode()307 mode_t File::mode ()
308 {
309   struct stat s;
310   if (!stat (_data.c_str (), &s))
311     return s.st_mode;
312 
313   return 0;
314 }
315 
316 ////////////////////////////////////////////////////////////////////////////////
size() const317 size_t File::size () const
318 {
319   struct stat s;
320   if (!stat (_data.c_str (), &s))
321     return s.st_size;
322 
323   return 0;
324 }
325 
326 ////////////////////////////////////////////////////////////////////////////////
mtime() const327 time_t File::mtime () const
328 {
329   struct stat s;
330   if (!stat (_data.c_str (), &s))
331     return s.st_mtime;
332 
333   return 0;
334 }
335 
336 ////////////////////////////////////////////////////////////////////////////////
ctime() const337 time_t File::ctime () const
338 {
339   struct stat s;
340   if (!stat (_data.c_str (), &s))
341     return s.st_ctime;
342 
343   return 0;
344 }
345 
346 ////////////////////////////////////////////////////////////////////////////////
btime() const347 time_t File::btime () const
348 {
349   struct stat s;
350   if (!stat (_data.c_str (), &s))
351 #ifdef HAVE_ST_BIRTHTIME
352     return s.st_birthtime;
353 #else
354     return s.st_ctime;
355 #endif
356 
357   return 0;
358 }
359 
360 ////////////////////////////////////////////////////////////////////////////////
create(const std::string & name,int mode)361 bool File::create (const std::string& name, int mode /* = 0640 */)
362 {
363   std::string full_name = expand (name);
364   std::ofstream out (full_name.c_str ());
365   if (out.good ())
366   {
367     out.close ();
368     chmod (full_name.c_str (), mode);
369     return true;
370   }
371 
372   return false;
373 }
374 
375 ////////////////////////////////////////////////////////////////////////////////
read(const std::string & name)376 std::string File::read (const std::string& name)
377 {
378   std::string contents = "";
379 
380   std::ifstream in (name.c_str ());
381   if (in.good ())
382   {
383     std::string line;
384     line.reserve (1024);
385     while (getline (in, line))
386       contents += line + "\n";
387 
388     in.close ();
389   }
390 
391   return contents;
392 }
393 
394 ////////////////////////////////////////////////////////////////////////////////
read(const std::string & name,std::string & contents)395 bool File::read (const std::string& name, std::string& contents)
396 {
397   contents = "";
398 
399   std::ifstream in (name.c_str ());
400   if (in.good ())
401   {
402     std::string line;
403     line.reserve (1024);
404     while (getline (in, line))
405       contents += line + "\n";
406 
407     in.close ();
408     return true;
409   }
410 
411   return false;
412 }
413 
414 ////////////////////////////////////////////////////////////////////////////////
read(const std::string & name,std::vector<std::string> & contents)415 bool File::read (const std::string& name, std::vector <std::string>& contents)
416 {
417   contents.clear ();
418 
419   std::ifstream in (name.c_str ());
420   if (in.good ())
421   {
422     std::string line;
423     line.reserve (1024);
424     while (getline (in, line))
425       contents.push_back (line);
426 
427     in.close ();
428     return true;
429   }
430 
431   return false;
432 }
433 
434 ////////////////////////////////////////////////////////////////////////////////
write(const std::string & name,const std::string & contents)435 bool File::write (const std::string& name, const std::string& contents)
436 {
437   std::ofstream out (expand (name).c_str (),
438                      std::ios_base::out | std::ios_base::trunc);
439   if (out.good ())
440   {
441     out << contents;
442     out.close ();
443     return true;
444   }
445 
446   return false;
447 }
448 
449 ////////////////////////////////////////////////////////////////////////////////
write(const std::string & name,const std::vector<std::string> & lines,bool addNewlines)450 bool File::write (
451   const std::string& name,
452   const std::vector <std::string>& lines,
453   bool addNewlines /* = true */)
454 {
455   std::ofstream out (expand (name).c_str (),
456                      std::ios_base::out | std::ios_base::trunc);
457   if (out.good ())
458   {
459     std::vector <std::string>::const_iterator it;
460     for (it = lines.begin (); it != lines.end (); ++it)
461     {
462       out << *it;
463 
464       if (addNewlines)
465         out << "\n";
466     }
467 
468     out.close ();
469     return true;
470   }
471 
472   return false;
473 }
474 
475 ////////////////////////////////////////////////////////////////////////////////
append(const std::string & name,const std::string & contents)476 bool File::append (const std::string& name, const std::string& contents)
477 {
478   std::ofstream out (expand (name).c_str (),
479                      std::ios_base::out | std::ios_base::app);
480   if (out.good ())
481   {
482     out << contents;
483     out.close ();
484     return true;
485   }
486 
487   return false;
488 }
489 
490 ////////////////////////////////////////////////////////////////////////////////
append(const std::string & name,const std::vector<std::string> & lines,bool addNewlines)491 bool File::append (
492   const std::string& name,
493   const std::vector <std::string>& lines,
494   bool addNewlines /* = true */)
495 {
496   std::ofstream out (expand (name).c_str (),
497                      std::ios_base::out | std::ios_base::app);
498   if (out.good ())
499   {
500     std::vector <std::string>::const_iterator it;
501     for (it = lines.begin (); it != lines.end (); ++it)
502     {
503       out << *it;
504 
505       if (addNewlines)
506         out << "\n";
507     }
508 
509     out.close ();
510     return true;
511   }
512 
513   return false;
514 }
515 
516 ////////////////////////////////////////////////////////////////////////////////
remove(const std::string & name)517 bool File::remove (const std::string& name)
518 {
519   return unlink (expand (name).c_str ()) == 0 ? true : false;
520 }
521 
522 ////////////////////////////////////////////////////////////////////////////////
523 
524