summaryrefslogtreecommitdiffstats
path: root/src/preprocessor.vala
blob: 6953aa5e038474c9690f3cc07b53ecbfcbb58466 (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
namespace Wixl {

    class Preprocessor: Object {

        HashTable<string, string> variables;
        construct {
            variables = new HashTable<string, string> (str_hash, str_equal);
        }

        public void define_variable (string name, string value) {
            variables.insert (name, value);
        }

        public string get_variable (string str, File? file) throws GLib.Error {
            var var = str.split (".", 2);
            if (var.length != 2)
                throw new Wixl.Error.FAILED ("invalid variable %s", str);

            switch (var[0]) {
            case "var":
                return variables.lookup (var[1]);
            case "env":
                return Environment.get_variable (var[1]);
            case "sys":
                switch (var[1]) {
                case "CURRENTDIR":
                    return Environment.get_current_dir ();
                case "SOURCEFILEDIR":
                    return file.get_basename ();
                case "SOURCEFILEPATH":
                    return file.get_path ();
                }
                break;
            }

            throw new Wixl.Error.FIXME ("unhandled variable type %s", str);
        }

        public string eval (string str, File? file) throws GLib.Error {
            var result = "";
            int end = 0;
            int pos = 0;

            while ((pos = str.index_of ("$", end)) != -1) {
                if (end < pos)
                    result += str[end:pos];
                end = pos + 1;
                var remainder = str[end:str.length];
                if (remainder.has_prefix ("$"))
                    result += "$";
                else if (remainder.has_prefix ("(")) {
                    var closing = find_closing_paren (remainder);
                    if (closing == -1)
                        throw new Wixl.Error.FAILED ("no matching closing parenthesis");
                    var substring = remainder[1:closing];
                    if (substring.index_of ("(") != -1)
                        throw new Wixl.Error.FIXME ("unsupported function");
                    result += get_variable (substring, file);
                    end += closing + 1;
                }
            }

            return result + str[end:str.length];
        }

        public Xml.Doc preprocess (string data, File? file) throws GLib.Error {
            Xml.Doc doc;
            var writer = new Xml.TextWriter.doc (out doc);
            var reader = new Xml.TextReader.for_doc (data, "");

            writer.start_document ();
            while (reader.read () > 0) {
                switch (reader.node_type ()) {
                case Xml.ReaderType.PROCESSING_INSTRUCTION:
                    switch (reader.const_local_name ()) {
                    case "define":
                        MatchInfo info;
                        var r = /^\s*(?P<name>.+?)\s*=\s*(?P<value>.+?)\s*$/;
                        if (r.match (reader.const_value (), 0, out info)) {
                            var name = remove_prefix ("var.", info.fetch_named ("name"));
                            var value = unquote (info.fetch_named ("value"));
                            define_variable (name, value);
                        } else
                            throw new Wixl.Error.FAILED ("invalid define");
                        break;
                    default:
                        warning ("unhandled preprocessor instruction %s", reader.const_local_name ());
                        break;
                    }
                    break;
                case Xml.ReaderType.ELEMENT:
                    var empty = reader.is_empty_element () > 0;

                    writer.start_element (reader.const_name ());
                    while (reader.move_to_next_attribute () > 0) {
                        var value = eval (reader.const_value (), file);
                        writer.write_attribute (reader.const_name (), value);
                    }

                    if (empty)
                        writer.end_element ();
                    break;
                case Xml.ReaderType.END_ELEMENT:
                    writer.end_element ();
                    break;
                }
            }
            writer.end_document ();

            return doc;
        }
    }
}