~ubuntu-branches/ubuntu/trusty/bmake/trusty-proposed

« back to all changes in this revision

Viewing changes to for.c

  • Committer: Package Import Robot
  • Author(s): Andrew Shadura
  • Date: 2013-09-22 16:07:33 UTC
  • Revision ID: package-import@ubuntu.com-20130922160733-9cvmsi7z0jswtrbi
Tags: upstream-20130904
ImportĀ upstreamĀ versionĀ 20130904

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*      $NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $     */
 
2
 
 
3
/*
 
4
 * Copyright (c) 1992, The Regents of the University of California.
 
5
 * All rights reserved.
 
6
 *
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted provided that the following conditions
 
9
 * are met:
 
10
 * 1. Redistributions of source code must retain the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer.
 
12
 * 2. Redistributions in binary form must reproduce the above copyright
 
13
 *    notice, this list of conditions and the following disclaimer in the
 
14
 *    documentation and/or other materials provided with the distribution.
 
15
 * 3. Neither the name of the University nor the names of its contributors
 
16
 *    may be used to endorse or promote products derived from this software
 
17
 *    without specific prior written permission.
 
18
 *
 
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
29
 * SUCH DAMAGE.
 
30
 */
 
31
 
 
32
#ifndef MAKE_NATIVE
 
33
static char rcsid[] = "$NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $";
 
34
#else
 
35
#include <sys/cdefs.h>
 
36
#ifndef lint
 
37
#if 0
 
38
static char sccsid[] = "@(#)for.c       8.1 (Berkeley) 6/6/93";
 
39
#else
 
40
__RCSID("$NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $");
 
41
#endif
 
42
#endif /* not lint */
 
43
#endif
 
44
 
 
45
/*-
 
46
 * for.c --
 
47
 *      Functions to handle loops in a makefile.
 
48
 *
 
49
 * Interface:
 
50
 *      For_Eval        Evaluate the loop in the passed line.
 
51
 *      For_Run         Run accumulated loop
 
52
 *
 
53
 */
 
54
 
 
55
#include    <assert.h>
 
56
#include    <ctype.h>
 
57
 
 
58
#include    "make.h"
 
59
#include    "hash.h"
 
60
#include    "dir.h"
 
61
#include    "buf.h"
 
62
#include    "strlist.h"
 
63
 
 
64
#define FOR_SUB_ESCAPE_CHAR  1
 
65
#define FOR_SUB_ESCAPE_BRACE 2
 
66
#define FOR_SUB_ESCAPE_PAREN 4
 
67
 
 
68
/*
 
69
 * For statements are of the form:
 
70
 *
 
71
 * .for <variable> in <varlist>
 
72
 * ...
 
73
 * .endfor
 
74
 *
 
75
 * The trick is to look for the matching end inside for for loop
 
76
 * To do that, we count the current nesting level of the for loops.
 
77
 * and the .endfor statements, accumulating all the statements between
 
78
 * the initial .for loop and the matching .endfor;
 
79
 * then we evaluate the for loop for each variable in the varlist.
 
80
 *
 
81
 * Note that any nested fors are just passed through; they get handled
 
82
 * recursively in For_Eval when we're expanding the enclosing for in
 
83
 * For_Run.
 
84
 */
 
85
 
 
86
static int        forLevel = 0;         /* Nesting level        */
 
87
 
 
88
/*
 
89
 * State of a for loop.
 
90
 */
 
91
typedef struct _For {
 
92
    Buffer        buf;                  /* Body of loop         */
 
93
    strlist_t     vars;                 /* Iteration variables  */
 
94
    strlist_t     items;                /* Substitution items */
 
95
    char          *parse_buf;
 
96
    int           short_var;
 
97
    int           sub_next;
 
98
} For;
 
99
 
 
100
static For        *accumFor;            /* Loop being accumulated */
 
101
 
 
102
 
 
103
 
 
104
static char *
 
105
make_str(const char *ptr, int len)
 
106
{
 
107
        char *new_ptr;
 
108
 
 
109
        new_ptr = bmake_malloc(len + 1);
 
110
        memcpy(new_ptr, ptr, len);
 
111
        new_ptr[len] = 0;
 
112
        return new_ptr;
 
113
}
 
114
 
 
115
static void
 
116
For_Free(For *arg)
 
117
{
 
118
    Buf_Destroy(&arg->buf, TRUE);
 
119
    strlist_clean(&arg->vars);
 
120
    strlist_clean(&arg->items);
 
121
    free(arg->parse_buf);
 
122
 
 
123
    free(arg);
 
124
}
 
125
 
 
126
/*-
 
127
 *-----------------------------------------------------------------------
 
128
 * For_Eval --
 
129
 *      Evaluate the for loop in the passed line. The line
 
130
 *      looks like this:
 
131
 *          .for <variable> in <varlist>
 
132
 *
 
133
 * Input:
 
134
 *      line            Line to parse
 
135
 *
 
136
 * Results:
 
137
 *      0: Not a .for statement, parse the line
 
138
 *      1: We found a for loop
 
139
 *     -1: A .for statement with a bad syntax error, discard.
 
140
 *
 
141
 * Side Effects:
 
142
 *      None.
 
143
 *
 
144
 *-----------------------------------------------------------------------
 
145
 */
 
146
int
 
147
For_Eval(char *line)
 
148
{
 
149
    For *new_for;
 
150
    char *ptr = line, *sub;
 
151
    int len;
 
152
    int escapes;
 
153
    unsigned char ch;
 
154
    char **words, *word_buf;
 
155
    int n, nwords;
 
156
 
 
157
    /* Skip the '.' and any following whitespace */
 
158
    for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
 
159
        continue;
 
160
 
 
161
    /*
 
162
     * If we are not in a for loop quickly determine if the statement is
 
163
     * a for.
 
164
     */
 
165
    if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
 
166
            !isspace((unsigned char) ptr[3])) {
 
167
        if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
 
168
            Parse_Error(PARSE_FATAL, "for-less endfor");
 
169
            return -1;
 
170
        }
 
171
        return 0;
 
172
    }
 
173
    ptr += 3;
 
174
 
 
175
    /*
 
176
     * we found a for loop, and now we are going to parse it.
 
177
     */
 
178
 
 
179
    new_for = bmake_malloc(sizeof *new_for);
 
180
    memset(new_for, 0, sizeof *new_for);
 
181
 
 
182
    /* Grab the variables. Terminate on "in". */
 
183
    for (;; ptr += len) {
 
184
        while (*ptr && isspace((unsigned char) *ptr))
 
185
            ptr++;
 
186
        if (*ptr == '\0') {
 
187
            Parse_Error(PARSE_FATAL, "missing `in' in for");
 
188
            For_Free(new_for);
 
189
            return -1;
 
190
        }
 
191
        for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
 
192
            continue;
 
193
        if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
 
194
            ptr += 2;
 
195
            break;
 
196
        }
 
197
        if (len == 1)
 
198
            new_for->short_var = 1;
 
199
        strlist_add_str(&new_for->vars, make_str(ptr, len), len);
 
200
    }
 
201
 
 
202
    if (strlist_num(&new_for->vars) == 0) {
 
203
        Parse_Error(PARSE_FATAL, "no iteration variables in for");
 
204
        For_Free(new_for);
 
205
        return -1;
 
206
    }
 
207
 
 
208
    while (*ptr && isspace((unsigned char) *ptr))
 
209
        ptr++;
 
210
 
 
211
    /*
 
212
     * Make a list with the remaining words
 
213
     * The values are substituted as ${:U<value>...} so we must \ escape
 
214
     * characters that break that syntax.
 
215
     * Variables are fully expanded - so it is safe for escape $.
 
216
     * We can't do the escapes here - because we don't know whether
 
217
     * we are substuting into ${...} or $(...).
 
218
     */
 
219
    sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
 
220
 
 
221
    /*
 
222
     * Split into words allowing for quoted strings.
 
223
     */
 
224
    words = brk_string(sub, &nwords, FALSE, &word_buf);
 
225
 
 
226
    free(sub);
 
227
    
 
228
    if (words != NULL) {
 
229
        for (n = 0; n < nwords; n++) {
 
230
            ptr = words[n];
 
231
            if (!*ptr)
 
232
                continue;
 
233
            escapes = 0;
 
234
            while ((ch = *ptr++)) {
 
235
                switch(ch) {
 
236
                case ':':
 
237
                case '$':
 
238
                case '\\':
 
239
                    escapes |= FOR_SUB_ESCAPE_CHAR;
 
240
                    break;
 
241
                case ')':
 
242
                    escapes |= FOR_SUB_ESCAPE_PAREN;
 
243
                    break;
 
244
                case /*{*/ '}':
 
245
                    escapes |= FOR_SUB_ESCAPE_BRACE;
 
246
                    break;
 
247
                }
 
248
            }
 
249
            /*
 
250
             * We have to dup words[n] to maintain the semantics of
 
251
             * strlist.
 
252
             */
 
253
            strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes);
 
254
        }
 
255
 
 
256
        free(words);
 
257
        free(word_buf);
 
258
 
 
259
        if ((len = strlist_num(&new_for->items)) > 0 &&
 
260
            len % (n = strlist_num(&new_for->vars))) {
 
261
            Parse_Error(PARSE_FATAL,
 
262
                        "Wrong number of words (%d) in .for substitution list"
 
263
                        " with %d vars", len, n);
 
264
            /*
 
265
             * Return 'success' so that the body of the .for loop is
 
266
             * accumulated.
 
267
             * Remove all items so that the loop doesn't iterate.
 
268
             */
 
269
            strlist_clean(&new_for->items);
 
270
        }
 
271
    }
 
272
 
 
273
    Buf_Init(&new_for->buf, 0);
 
274
    accumFor = new_for;
 
275
    forLevel = 1;
 
276
    return 1;
 
277
}
 
278
 
 
279
/*
 
280
 * Add another line to a .for loop.
 
281
 * Returns 0 when the matching .endfor is reached.
 
282
 */
 
283
 
 
284
int
 
285
For_Accum(char *line)
 
286
{
 
287
    char *ptr = line;
 
288
 
 
289
    if (*ptr == '.') {
 
290
 
 
291
        for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
 
292
            continue;
 
293
 
 
294
        if (strncmp(ptr, "endfor", 6) == 0 &&
 
295
                (isspace((unsigned char) ptr[6]) || !ptr[6])) {
 
296
            if (DEBUG(FOR))
 
297
                (void)fprintf(debug_file, "For: end for %d\n", forLevel);
 
298
            if (--forLevel <= 0)
 
299
                return 0;
 
300
        } else if (strncmp(ptr, "for", 3) == 0 &&
 
301
                 isspace((unsigned char) ptr[3])) {
 
302
            forLevel++;
 
303
            if (DEBUG(FOR))
 
304
                (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
 
305
        }
 
306
    }
 
307
 
 
308
    Buf_AddBytes(&accumFor->buf, strlen(line), line);
 
309
    Buf_AddByte(&accumFor->buf, '\n');
 
310
    return 1;
 
311
}
 
312
 
 
313
 
 
314
/*-
 
315
 *-----------------------------------------------------------------------
 
316
 * For_Run --
 
317
 *      Run the for loop, imitating the actions of an include file
 
318
 *
 
319
 * Results:
 
320
 *      None.
 
321
 *
 
322
 * Side Effects:
 
323
 *      None.
 
324
 *
 
325
 *-----------------------------------------------------------------------
 
326
 */
 
327
 
 
328
static int
 
329
for_var_len(const char *var)
 
330
{
 
331
    char ch, var_start, var_end;
 
332
    int depth;
 
333
    int len;
 
334
 
 
335
    var_start = *var;
 
336
    if (var_start == 0)
 
337
        /* just escape the $ */
 
338
        return 0;
 
339
 
 
340
    if (var_start == '(')
 
341
        var_end = ')';
 
342
    else if (var_start == '{')
 
343
        var_end = '}';
 
344
    else
 
345
        /* Single char variable */
 
346
        return 1;
 
347
 
 
348
    depth = 1;
 
349
    for (len = 1; (ch = var[len++]) != 0;) {
 
350
        if (ch == var_start)
 
351
            depth++;
 
352
        else if (ch == var_end && --depth == 0)
 
353
            return len;
 
354
    }
 
355
 
 
356
    /* Variable end not found, escape the $ */
 
357
    return 0;
 
358
}
 
359
 
 
360
static void
 
361
for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
 
362
{
 
363
    const char *item = strlist_str(items, item_no);
 
364
    int len;
 
365
    char ch;
 
366
 
 
367
    /* If there were no escapes, or the only escape is the other variable
 
368
     * terminator, then just substitute the full string */
 
369
    if (!(strlist_info(items, item_no) &
 
370
            (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) {
 
371
        Buf_AddBytes(cmds, strlen(item), item);
 
372
        return;
 
373
    }
 
374
 
 
375
    /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
 
376
    while ((ch = *item++) != 0) {
 
377
        if (ch == '$') {
 
378
            len = for_var_len(item);
 
379
            if (len != 0) {
 
380
                Buf_AddBytes(cmds, len + 1, item - 1);
 
381
                item += len;
 
382
                continue;
 
383
            }
 
384
            Buf_AddByte(cmds, '\\');
 
385
        } else if (ch == ':' || ch == '\\' || ch == ech)
 
386
            Buf_AddByte(cmds, '\\');
 
387
        Buf_AddByte(cmds, ch);
 
388
    }
 
389
}
 
390
 
 
391
static char *
 
392
For_Iterate(void *v_arg, size_t *ret_len)
 
393
{
 
394
    For *arg = v_arg;
 
395
    int i, len;
 
396
    char *var;
 
397
    char *cp;
 
398
    char *cmd_cp;
 
399
    char *body_end;
 
400
    char ch;
 
401
    Buffer cmds;
 
402
 
 
403
    if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
 
404
        /* No more iterations */
 
405
        For_Free(arg);
 
406
        return NULL;
 
407
    }
 
408
 
 
409
    free(arg->parse_buf);
 
410
    arg->parse_buf = NULL;
 
411
 
 
412
    /*
 
413
     * Scan the for loop body and replace references to the loop variables
 
414
     * with variable references that expand to the required text.
 
415
     * Using variable expansions ensures that the .for loop can't generate
 
416
     * syntax, and that the later parsing will still see a variable.
 
417
     * We assume that the null variable will never be defined.
 
418
     *
 
419
     * The detection of substitions of the loop control variable is naive.
 
420
     * Many of the modifiers use \ to escape $ (not $) so it is possible
 
421
     * to contrive a makefile where an unwanted substitution happens.
 
422
     */
 
423
 
 
424
    cmd_cp = Buf_GetAll(&arg->buf, &len);
 
425
    body_end = cmd_cp + len;
 
426
    Buf_Init(&cmds, len + 256);
 
427
    for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) {
 
428
        char ech;
 
429
        ch = *++cp;
 
430
        if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) {
 
431
            cp++;
 
432
            /* Check variable name against the .for loop variables */
 
433
            STRLIST_FOREACH(var, &arg->vars, i) {
 
434
                len = strlist_info(&arg->vars, i);
 
435
                if (memcmp(cp, var, len) != 0)
 
436
                    continue;
 
437
                if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\')
 
438
                    continue;
 
439
                /* Found a variable match. Replace with :U<value> */
 
440
                Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
 
441
                Buf_AddBytes(&cmds, 2, ":U");
 
442
                cp += len;
 
443
                cmd_cp = cp;
 
444
                for_substitute(&cmds, &arg->items, arg->sub_next + i, ech);
 
445
                break;
 
446
            }
 
447
            continue;
 
448
        }
 
449
        if (ch == 0)
 
450
            break;
 
451
        /* Probably a single character name, ignore $$ and stupid ones. {*/
 
452
        if (!arg->short_var || strchr("}):$", ch) != NULL) {
 
453
            cp++;
 
454
            continue;
 
455
        }
 
456
        STRLIST_FOREACH(var, &arg->vars, i) {
 
457
            if (var[0] != ch || var[1] != 0)
 
458
                continue;
 
459
            /* Found a variable match. Replace with ${:U<value>} */
 
460
            Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
 
461
            Buf_AddBytes(&cmds, 3, "{:U");
 
462
            cmd_cp = ++cp;
 
463
            for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
 
464
            Buf_AddBytes(&cmds, 1, "}");
 
465
            break;
 
466
        }
 
467
    }
 
468
    Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp);
 
469
 
 
470
    cp = Buf_Destroy(&cmds, FALSE);
 
471
    if (DEBUG(FOR))
 
472
        (void)fprintf(debug_file, "For: loop body:\n%s", cp);
 
473
 
 
474
    arg->sub_next += strlist_num(&arg->vars);
 
475
 
 
476
    arg->parse_buf = cp;
 
477
    *ret_len = strlen(cp);
 
478
    return cp;
 
479
}
 
480
 
 
481
void
 
482
For_Run(int lineno)
 
483
 
484
    For *arg;
 
485
  
 
486
    arg = accumFor;
 
487
    accumFor = NULL;
 
488
 
 
489
    if (strlist_num(&arg->items) == 0) {
 
490
        /* Nothing to expand - possibly due to an earlier syntax error. */
 
491
        For_Free(arg);
 
492
        return;
 
493
    }
 
494
 
 
495
    Parse_SetInput(NULL, lineno, -1, For_Iterate, arg);
 
496
}