1 #include <cstdio>
2 #include <cstring>
3 #include <cctype>
4 #include <ctime>                                    // borland needs this
5 #include <sys/stat.h>
6 #if defined(_MSC_VER)
7 #    include <sys/utime.h>
8 #else
9 #    include <utime.h>
10 #endif
11 #if defined(_WIN32)
12 #    include <io.h>
13 #    define F_OK 0
14 #else
15 #    include <unistd.h>
16 #endif
17 #include "sedit.h"
18 #include "setfname.h"
19 
20 /*
21 
22   copyright (c) 2004-2006 squell <squell@alumina.nl>
23 
24   use, modification, copying and distribution of this software is permitted
25   under the conditions described in the file 'COPYING'.
26 
27 */
28 
29 using namespace std;
30 
31 using tag::write::file;
32 
33     // checks if a character is a member of the portable set
34 
35 namespace {
36     const char allowed[] = " ._-()";
portable_fn(char c)37     bool portable_fn(char c)
38     {
39         return isalnum(c) || (c & 0x80) || strchr(allowed, c);
40     }
41 
get_utime(const char * fname,utimbuf * buf)42     utimbuf* get_utime(const char* fname, utimbuf* buf)
43     {
44         struct stat file_info;
45         if( stat(fname, &file_info) == 0 ) {
46             buf->actime  = file_info.st_atime;
47             buf->modtime = file_info.st_mtime;
48             return buf;
49         }
50         return 0;
51     }
52 }
53 
vmodify(const char * fname,const function & edit) const54 bool file::vmodify(const char* fname, const function& edit) const
55 {
56     utimbuf buf, *stamp = get_utime(fname, &buf);
57 
58     function::result edited = edit(m_template);    // use pre-values
59 
60     if(m_template.empty() || !edited.good()) {
61         bool ok = combined<interface>::vmodify(fname, edit);
62         if(m_preserve) utime(fname, stamp);
63         return ok;
64     }
65 
66     string name = edited;
67     for(string::iterator p = name.begin(); p != name.end(); ++p) {
68         if(!portable_fn(*p)) *p = '_';             // replace ill. chars
69     }
70     if(const char* psep = strrchr(fname, '/')) {
71         name.insert(0, fname, psep-fname+1);       // copy path prefix
72     }
73 
74     const char* newfn = name.c_str();
75     if(name != fname && access(newfn, F_OK) == 0)  // check if already exists
76         throw failure(newfn, ": file already exists");
77 
78     bool ok = combined<interface>::vmodify(fname, edit);
79     if(m_preserve) utime(fname, stamp);
80 
81     if(ok && std::rename(fname, newfn) != 0)
82         throw failure("could not rename ", fname);
83 
84     return ok;
85 }
86 
87