vinyl-cache/lib/libvcc/vcc_source.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include "config.h"
32
33
#include <glob.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
38
#include "vcc_compile.h"
39
40
#include "vfil.h"
41
42
struct source *
43 102666
vcc_new_source(const char *src, const char *kind, const char *name)
44
{
45
        struct source *sp;
46
47 102666
        AN(src);
48 102666
        AN(name);
49 102666
        ALLOC_OBJ(sp, SOURCE_MAGIC);
50 102666
        AN(sp);
51 102666
        REPLACE(sp->name, name);
52 102666
        sp->kind = kind;
53 102666
        sp->b = src;
54 102666
        sp->e = strchr(src, '\0');
55 102666
        VTAILQ_INIT(&sp->src_tokens);
56 102666
        return (sp);
57
}
58
59
/*--------------------------------------------------------------------*/
60
61
struct source *
62 1218
vcc_file_source(struct vcc *tl, const char *fn)
63
{
64
        char *f, *fnp;
65
        struct source *sp;
66
67 1218
        if (!tl->unsafe_path && strchr(fn, '/') != NULL) {
68 21
                VSB_printf(tl->sb, "VCL filename '%s' is unsafe.\n", fn);
69 21
                tl->err = 1;
70 21
                return (NULL);
71
        }
72 1197
        f = NULL;
73 1197
        if (VFIL_searchpath(tl->vcl_path, NULL, &f, fn, &fnp) || f == NULL) {
74 42
                VSB_printf(tl->sb, "Cannot read file '%s' (%s)\n",
75 21
                    fnp != NULL ? fnp : fn, strerror(errno));
76 21
                free(fnp);
77 21
                tl->err = 1;
78 21
                return (NULL);
79
        }
80 1176
        sp = vcc_new_source(f, "file", fnp);
81 1176
        free(fnp);
82 1176
        return (sp);
83 1218
}
84
85
/*--------------------------------------------------------------------*/
86
87
static void
88 903
vcc_include_file(struct vcc *tl, const struct source *src_sp,
89
    const char *filename, const struct token *parent_token)
90
{
91
        struct source *sp;
92
93 903
        sp = vcc_file_source(tl, filename);
94 903
        if (sp == NULL)
95 21
                return;
96
97 882
        sp->parent = src_sp;
98 882
        sp->parent_tok = parent_token;
99 882
        vcc_lex_source(tl, sp, 0);
100 903
}
101
102
/*--------------------------------------------------------------------*/
103
104
static void
105 105
vcc_include_glob_file(struct vcc *tl, const struct source *src_sp,
106
    const char *filename, const struct token *parent_token)
107
{
108
        glob_t  g[1];
109
        unsigned u;
110
        int i;
111
112 105
        if (filename[0] != '/' && (filename[0] != '.' || filename[1] != '/')) {
113 21
                VSB_cat(tl->sb,
114
                    "+glob can only be used with absolute paths or relative "
115
                    "paths starting with './'\n");
116 21
                tl->err = 1;
117 21
                return;
118
        }
119 84
        memset(g, 0, sizeof g);
120 84
        i = glob(filename, 0, NULL, g);
121 84
        switch (i) {
122
        case 0:
123 168
                for (u = 0; !tl->err && u < g->gl_pathc; u++) {
124 105
                        vcc_include_file(
125 105
                            tl, src_sp, g->gl_pathv[u], parent_token);
126 105
                }
127 63
                break;
128
        case GLOB_NOMATCH:
129 21
                VSB_cat(tl->sb, "glob pattern matched no files.\n");
130 21
                tl->err = 1;
131 21
                break;
132
        default:
133 0
                VSB_printf(tl->sb, "glob(3) expansion failed (%d)\n", i);
134 0
                tl->err = 1;
135 0
                break;
136
        }
137 84
        globfree(g);
138 105
}
139
140
/*--------------------------------------------------------------------
141
 * NB: We cannot use vcc_ErrWhere2() on tokens which are not on the
142
 * NB: tl->tokens list.
143
 */
144
145
static struct token *
146 1092
vcc_lex_include(struct vcc *tl, const struct source *src_sp, struct token *t)
147
{
148
        struct token *tok1;
149 1092
        int i, glob_flag = 0;
150 1092
        struct vsb *vsb = NULL;
151
        const char *filename;
152
        const char *p;
153
154 1092
        assert(vcc_IdIs(t, "include"));
155
156 1092
        tok1 = VTAILQ_NEXT(t, src_list);
157 1092
        AN(tok1);
158
159 1239
        while (tok1->tok != EOI) {
160 1176
                t = VTAILQ_NEXT(tok1, src_list);
161 1176
                AN(t);
162 1176
                i = vcc_IsFlagRaw(tl, tok1, t);
163 1176
                if (i < 0)
164 1029
                        break;
165 147
                if (vcc_IdIs(t, "glob")) {
166 147
                        glob_flag = i;
167 147
                } else {
168 0
                        VSB_cat(tl->sb, "Unknown include flag:\n");
169 0
                        vcc_ErrWhere(tl, t);
170 0
                        return (t);
171
                }
172 147
                tok1 = VTAILQ_NEXT(t, src_list);
173 147
                AN(tok1);
174
        }
175
176 1092
        if (tok1->tok != CSTR) {
177 126
                VSB_cat(tl->sb,
178
                    "include not followed by string constant.\n");
179 126
                vcc_ErrWhere(tl, tok1);
180 126
                return (t);
181
        }
182 966
        t = VTAILQ_NEXT(tok1, src_list);
183 966
        AN(t);
184
185 966
        if (t->tok != ';') {
186 21
                VSB_cat(tl->sb,
187
                    "include <string> not followed by semicolon.\n");
188 21
                vcc_ErrWhere(tl, tok1);
189 21
                return (t);
190
        }
191
192 945
        filename = tok1->dec;
193
194 945
        if (filename[0] == '.' && filename[1] == '/') {
195
                /*
196
                 * Nested include filenames, starting with "./" are
197
                 * resolved relative to the VCL file which contains
198
                 * the include directive.
199
                 */
200 210
                if (src_sp->name[0] != '/') {
201 42
                        VSB_cat(tl->sb,
202
                            "include \"./xxxxx\"; needs absolute "
203
                            "filename of including file.\n");
204 42
                        vcc_ErrWhere(tl, tok1);
205 42
                        return (t);
206
                }
207 168
                vsb = VSB_new_auto();
208 168
                AN(vsb);
209 168
                p = strrchr(src_sp->name, '/');
210 168
                AN(p);
211 168
                VSB_bcat(vsb, src_sp->name, p - src_sp->name);
212 168
                VSB_cat(vsb, filename + 1);
213 168
                AZ(VSB_finish(vsb));
214 168
                filename = VSB_data(vsb);
215 168
        }
216
217 903
        if (glob_flag)
218 105
                vcc_include_glob_file(tl, src_sp, filename, tok1);
219
        else
220 798
                vcc_include_file(tl, src_sp, filename, tok1);
221 903
        if (vsb != NULL)
222 168
                VSB_destroy(&vsb);
223 903
        if (tl->err)
224 189
                vcc_ErrWhere(tl, tok1);
225 903
        return (t);
226 1092
}
227
228
void
229 102666
vcc_lex_source(struct vcc *tl, struct source *src_sp, int eoi)
230
{
231
        struct token *t;
232
        const struct source *sp1;
233
234 102666
        CHECK_OBJ_NOTNULL(src_sp, SOURCE_MAGIC);
235
236 103821
        for (sp1 = src_sp->parent; sp1 != NULL; sp1 = sp1->parent) {
237 1197
                if (!vstrcmp(sp1->name, src_sp->name) &&
238 42
                    !vstrcmp(sp1->kind, src_sp->kind)) {
239 84
                        VSB_printf(tl->sb,
240
                            "Recursive use of %s \"%s\"\n\n",
241 42
                            src_sp->kind, src_sp->name);
242 42
                        tl->err = 1;
243 42
                        return;
244
                }
245 1155
        }
246
247 102624
        VTAILQ_INSERT_TAIL(&tl->sources, src_sp, list);
248 102624
        src_sp->idx = tl->nsources++;
249
250 102624
        vcc_Lexer(tl, src_sp);
251 102624
        if (tl->err)
252 504
                return;
253 68659110
        VTAILQ_FOREACH(t, &src_sp->src_tokens, src_list) {
254 68591513
                if (!eoi && t->tok == EOI)
255 34145
                        break;
256
257
                /* Expand and lex any includes in the token string */
258 68557368
                if (t->tok == ID && vcc_IdIs(t, "include")) {
259 1092
                        t = vcc_lex_include(tl, src_sp, t);
260 1092
                } else {
261 68556276
                        VTAILQ_INSERT_TAIL(&tl->tokens, t, list);
262
                }
263 68557368
                if (tl->err)
264 378
                        return;
265 68556990
        }
266 102666
}