summary refs log tree commit diff
path: root/src/util.hh
blob: 9b3f212de3591fea1e20d69c3ccfe8925f030817 (plain) (blame)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#ifndef __UTIL_H
#define __UTIL_H

#include <string>
#include <vector>
#include <sstream>

#include <unistd.h>

extern "C" {
#include "md5.h"
}

using namespace std;


class Error : public exception
{
    string err;
public:
    Error(string _err) { err = _err; }
    ~Error() throw () { };
    const char * what() const throw () { return err.c_str(); }
};

class UsageError : public Error
{
public:
    UsageError(string _err) : Error(_err) { };
};

class BadRefError : public Error
{
public:
    BadRefError(string _err) : Error(_err) { };
};


typedef vector<string> Strings;


/* !!! the following shouldn't be here; abuse of the preprocessor */


/* The canonical system name, as returned by config.guess. */ 
static string thisSystem = SYSTEM;


/* The prefix of the Nix installation, and the environment variable
   that can be used to override the default. */
static string nixHomeDir = "/nix";
static string nixHomeDirEnvVar = "NIX";


string absPath(string filename, string dir = "")
{
    if (filename[0] != '/') {
        if (dir == "") {
            char buf[PATH_MAX];
            if (!getcwd(buf, sizeof(buf)))
                throw Error("cannot get cwd");
            dir = buf;
        }
        filename = dir + "/" + filename;
        /* !!! canonicalise */
        char resolved[PATH_MAX];
        if (!realpath(filename.c_str(), resolved))
            throw Error("cannot canonicalise path " + filename);
        filename = resolved;
    }
    return filename;
}


string printHash(unsigned char * buf)
{
    ostringstream str;
    for (int i = 0; i < 16; i++) {
        str.fill('0');
        str.width(2);
        str << hex << (int) buf[i];
    }
    return str.str();
}

    
/* Verify that a reference is valid (that is, is a MD5 hash code). */
bool isHash(const string & s)
{
    if (s.length() != 32) return false;
    for (int i = 0; i < 32; i++) {
        char c = s[i];
        if (!((c >= '0' && c <= '9') ||
              (c >= 'a' && c <= 'f')))
            return false;
    }
    return true;
}


void checkHash(const string & s)
{
    if (!isHash(s)) throw BadRefError("invalid reference: " + s);
}


/* Compute the MD5 hash of a file. */
string hashFile(string filename)
{
    unsigned char hash[16];
    FILE * file = fopen(filename.c_str(), "rb");
    if (!file)
        throw BadRefError("file `" + filename + "' does not exist");
    int err = md5_stream(file, hash);
    fclose(file);
    if (err) throw BadRefError("cannot hash file");
    return printHash(hash);
}



/* Return the directory part of the given path, i.e., everything
   before the final `/'. */
string dirOf(string s)
{
    unsigned int pos = s.rfind('/');
    if (pos == string::npos) throw Error("invalid file name");
    return string(s, 0, pos);
}


/* Return the base name of the given path, i.e., everything following
   the final `/'. */
string baseNameOf(string s)
{
    unsigned int pos = s.rfind('/');
    if (pos == string::npos) throw Error("invalid file name");
    return string(s, pos + 1);
}


#endif /* !__UTIL_H */