summaryrefslogtreecommitdiffabout
path: root/lib/util.cc
blob: 74039c6f28ee76e5a149ce4eea12a084bdea33bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <sys/types.h>
#include <sys/stat.h>
#include <konforka/util.h>

namespace konforka {

    /*
     * XXX: this code is borrowed from sitecing as is, although it should be optimized.
     */

    string normalize_path(const string& p,int o) {
	const char *s = p.c_str();
	if( s[0]=='.' && s[1]=='/' )
	    s += 2; // skip leading './'
	if(o&strip_leading_slash)
	    for(;(*s)=='/';s++);
	string rv;
	string::size_type notslash = 0;
	for(;*s;s++) {
	    if(s[0]=='/') {
		if(s[1]=='/')
		    continue; // skip duplicate slash
		if(s[1]=='.' && s[2]=='/') {
		    // '/./' sequence encountered
		    s += 2;
		    continue;
		}
	    }
	    if(
	       (o&restrict_dotdot) && (
		( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' ) // '^../'
		||
		( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]=='/' || s[3]==0) ) // '/../' or '/..$'
	       )
	      )
		throw restricted_sequence_error(CODEPOINT,"restricted updir (..) sequence encountered");
	    rv += *s;
	    if( (*s) !='/' )
		notslash = rv.length();
	}
	if(!(o&strip_trailing_slash))
	    notslash++;
	if(notslash<rv.length())
	    rv.erase(notslash); // XXX: does this operation have enough sense to be performed?
	return rv;
    }

    string dir_name(const string& p) {
	string::size_type sl = p.find_last_of('/');
	if(sl==string::npos)
	    return ""; // no slashes -- no dir.
	string::size_type nosl = p.find_last_not_of('/',sl);
	if(nosl==string::npos)
	    return ""; // only slashes -- no dir.
	return p.substr(0,nosl+1);
    }

    string combine_path(const string& orig,const string& rel,int o) {
	string r = normalize_path(rel,0);
	if(r.empty()) {
	    // XXX: this behaviour is questionable.
	    return normalize_path( (o&origin_is_file)?dir_name(orig):orig, strip_leading_slash|restrict_dotdot|strip_trailing_slash);
	}
	string rv;
	if(r[0]=='/') {
	    r.erase(0,1);
	}else{
	    rv = normalize_path( (o&origin_is_file)?dir_name(orig):orig, restrict_dotdot|strip_trailing_slash);
	}
	string::size_type lsl = rv.rfind('/');
	// iterate through slashes in relative path
	for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) {
	    assert(sl!=0); // sure we don't start with '/' at this point
	    if(sl==1 && r[0]=='.') { // './'
		r.erase(0,2);
	    }else if(sl==2 && r[0]=='.' && r[1]=='.') { // '../'
		if(lsl==string::npos) {
		    if(rv.empty() && (o&fail_beyond_root))
			throw beyond_root_error(CODEPOINT,"went beyond root while combining path");
		    rv.clear();
		}else{
		    rv.erase(lsl);
		    lsl = rv.rfind('/');
		}
		r.erase(0,3);
	    }else{ // 'something/'
		lsl = rv.length();
		rv += '/';
		rv += r.substr(0,sl);
		r.erase(0,sl+1);
	    }
	}
	if(r.empty())
	    return rv+'/';
	if(r.length()==2 && r[0]=='.' && r[1]=='.') {
	    if(lsl==string::npos) {
		if(rv.empty() && (o&fail_beyond_root))
		    throw beyond_root_error(CODEPOINT,"went beyond root while combining path");
		return "/";
	    }else{
		rv.erase(lsl+1);
		return rv;
	    }
	}
	rv += '/';
	rv += r;
	return rv;
    }

    void make_path(const string& p,mode_t m) {
	struct stat st;
	for(string::size_type sl=0;sl!=string::npos;sl=p.find('/',sl+1)) {
	    if(!sl)
		continue;
	    string pp = p.substr(0,sl);
	    if(stat(pp.c_str(),&st) || !S_ISDIR(st.st_mode)) {
		if(mkdir(pp.c_str(),m))
		    throw konforka::system_error(CODEPOINT,"failed to mkdir()");
	    }
	}
	if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) {
	    if(mkdir(p.c_str(),m))
		throw konforka::system_error(CODEPOINT,"failed to mkdir()");
	}
    }
    
}