[BACK]Return to for.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / make

Annotation of src/usr.bin/make/for.c, Revision 1.39

1.39    ! dsl         1: /*     $NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $     */
1.3       christos    2:
1.1       cgd         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.
1.15      agc        15:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        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:
1.17      ross       32: #ifndef MAKE_NATIVE
1.39    ! dsl        33: static char rcsid[] = "$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $";
1.6       lukem      34: #else
1.5       christos   35: #include <sys/cdefs.h>
1.1       cgd        36: #ifndef lint
1.3       christos   37: #if 0
1.4       christos   38: static char sccsid[] = "@(#)for.c      8.1 (Berkeley) 6/6/93";
1.3       christos   39: #else
1.39    ! dsl        40: __RCSID("$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $");
1.3       christos   41: #endif
1.1       cgd        42: #endif /* not lint */
1.6       lukem      43: #endif
1.1       cgd        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:
1.13      wiz        55: #include    <assert.h>
1.1       cgd        56: #include    <ctype.h>
1.13      wiz        57:
1.1       cgd        58: #include    "make.h"
                     59: #include    "hash.h"
                     60: #include    "dir.h"
                     61: #include    "buf.h"
1.38      dsl        62: #include    "strlist.h"
1.1       cgd        63:
                     64: /*
                     65:  * For statements are of the form:
                     66:  *
                     67:  * .for <variable> in <varlist>
                     68:  * ...
                     69:  * .endfor
                     70:  *
                     71:  * The trick is to look for the matching end inside for for loop
                     72:  * To do that, we count the current nesting level of the for loops.
                     73:  * and the .endfor statements, accumulating all the statements between
1.4       christos   74:  * the initial .for loop and the matching .endfor;
1.1       cgd        75:  * then we evaluate the for loop for each variable in the varlist.
1.7       christos   76:  *
                     77:  * Note that any nested fors are just passed through; they get handled
                     78:  * recursively in For_Eval when we're expanding the enclosing for in
                     79:  * For_Run.
1.1       cgd        80:  */
                     81:
                     82: static int       forLevel = 0;         /* Nesting level        */
                     83:
                     84: /*
                     85:  * State of a for loop.
                     86:  */
1.2       jtc        87: typedef struct _For {
1.7       christos   88:     Buffer       buf;                  /* Body of loop         */
1.38      dsl        89:     strlist_t     vars;                        /* Iteration variables  */
                     90:     strlist_t     items;               /* Substitution items */
1.2       jtc        91: } For;
1.1       cgd        92:
1.7       christos   93: static For        accumFor;             /* Loop being accumulated */
1.1       cgd        94:
                     95: 
1.7       christos   96:
1.33      dsl        97: static char *
                     98: make_str(const char *ptr, int len)
                     99: {
                    100:        char *new_ptr;
                    101:
                    102:        new_ptr = bmake_malloc(len + 1);
                    103:        memcpy(new_ptr, ptr, len);
                    104:        new_ptr[len] = 0;
                    105:        return new_ptr;
                    106: }
                    107:
1.7       christos  108: /*-
                    109:  *-----------------------------------------------------------------------
1.1       cgd       110:  * For_Eval --
                    111:  *     Evaluate the for loop in the passed line. The line
                    112:  *     looks like this:
                    113:  *         .for <variable> in <varlist>
                    114:  *
1.13      wiz       115:  * Input:
                    116:  *     line            Line to parse
                    117:  *
1.1       cgd       118:  * Results:
1.35      dsl       119:  *      0: Not a .for statement, parse the line
                    120:  *     1: We found a for loop
                    121:  *     -1: A .for statement with a bad syntax error, discard.
1.1       cgd       122:  *
                    123:  * Side Effects:
                    124:  *     None.
                    125:  *
                    126:  *-----------------------------------------------------------------------
                    127:  */
                    128: int
1.13      wiz       129: For_Eval(char *line)
1.1       cgd       130: {
1.33      dsl       131:     char *ptr = line, *sub;
                    132:     int len;
1.39    ! dsl       133:     int i;
        !           134:     int escapes;
1.33      dsl       135:
                    136:     /* Forget anything we previously knew about - it cannot be useful */
                    137:     memset(&accumFor, 0, sizeof accumFor);
1.32      dsl       138:
                    139:     forLevel = 0;
                    140:     for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
                    141:        continue;
                    142:     /*
                    143:      * If we are not in a for loop quickly determine if the statement is
                    144:      * a for.
                    145:      */
                    146:     if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
                    147:            !isspace((unsigned char) ptr[3])) {
                    148:        if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
                    149:            Parse_Error(PARSE_FATAL, "for-less endfor");
                    150:            return -1;
                    151:        }
                    152:        return 0;
                    153:     }
                    154:     ptr += 3;
1.1       cgd       155:
1.32      dsl       156:     /*
                    157:      * we found a for loop, and now we are going to parse it.
                    158:      */
1.1       cgd       159:
1.33      dsl       160:     /* Grab the variables. Terminate on "in". */
                    161:     for (;; ptr += len) {
1.32      dsl       162:        while (*ptr && isspace((unsigned char) *ptr))
                    163:            ptr++;
1.33      dsl       164:        if (*ptr == '\0') {
                    165:            Parse_Error(PARSE_FATAL, "missing `in' in for");
                    166:            return -1;
                    167:        }
                    168:        for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
                    169:            continue;
                    170:        if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
                    171:            ptr += 2;
                    172:            break;
                    173:        }
1.38      dsl       174:        strlist_add_str(&accumFor.vars, make_str(ptr, len));
1.32      dsl       175:     }
1.1       cgd       176:
1.38      dsl       177:     if (strlist_num(&accumFor.vars) == 0) {
1.32      dsl       178:        Parse_Error(PARSE_FATAL, "no iteration variables in for");
                    179:        return -1;
                    180:     }
1.4       christos  181:
1.32      dsl       182:     while (*ptr && isspace((unsigned char) *ptr))
                    183:        ptr++;
                    184:
                    185:     /*
                    186:      * Make a list with the remaining words
1.39    ! dsl       187:      * The values are substituted as ${:U<value>... so we must \ escape
        !           188:      * characters that break that syntax - particularly ':', maybe $ and \.
1.32      dsl       189:      */
                    190:     sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
1.4       christos  191:
1.38      dsl       192:     for (ptr = sub;; ptr += len) {
1.33      dsl       193:        while (*ptr && isspace((unsigned char)*ptr))
                    194:            ptr++;
                    195:        if (*ptr == 0)
                    196:            break;
1.39    ! dsl       197:        escapes = 0;
        !           198:        for (len = 0; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
        !           199:            if (ptr[len] == ':')
        !           200:                escapes++;
        !           201:        if (escapes == 0)
        !           202:            strlist_add_str(&accumFor.items, make_str(ptr, len));
        !           203:        else {
        !           204:            char *item = bmake_malloc(len + escapes + 1);
        !           205:            strlist_add_str(&accumFor.items, item);
        !           206:            for (i = 0; i < len; i++) {
        !           207:                if (ptr[i] == ':')
        !           208:                    *item++ = '\\';
        !           209:                *item++ = ptr[i];
        !           210:            }
        !           211:            *item = 0;
        !           212:        }
1.33      dsl       213:     }
1.1       cgd       214:
1.33      dsl       215:     free(sub);
1.7       christos  216:
1.38      dsl       217:     if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) {
1.33      dsl       218:        Parse_Error(PARSE_FATAL,
                    219:                "Wrong number of words in .for substitution list %d %d",
1.38      dsl       220:                strlist_num(&accumFor.items), strlist_num(&accumFor.vars));
1.33      dsl       221:        /*
                    222:         * Return 'success' so that the body of the .for loop is accumulated.
                    223:         * The loop will have zero iterations expanded due a later test.
                    224:         */
1.32      dsl       225:     }
                    226:
                    227:     accumFor.buf = Buf_Init(0);
                    228:     forLevel = 1;
                    229:     return 1;
                    230: }
1.7       christos  231:
1.35      dsl       232: /*
                    233:  * Add another line to a .for loop.
                    234:  * Returns 0 when the matching .enfor is reached.
                    235:  */
                    236:
1.32      dsl       237: int
                    238: For_Accum(char *line)
                    239: {
                    240:     char *ptr = line;
1.26      dsl       241:
                    242:     if (*ptr == '.') {
1.1       cgd       243:
1.2       jtc       244:        for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1       cgd       245:            continue;
                    246:
1.2       jtc       247:        if (strncmp(ptr, "endfor", 6) == 0 &&
1.26      dsl       248:                (isspace((unsigned char) ptr[6]) || !ptr[6])) {
1.1       cgd       249:            if (DEBUG(FOR))
1.23      dsl       250:                (void)fprintf(debug_file, "For: end for %d\n", forLevel);
1.32      dsl       251:            if (--forLevel <= 0)
1.1       cgd       252:                return 0;
1.26      dsl       253:        } else if (strncmp(ptr, "for", 3) == 0 &&
1.2       jtc       254:                 isspace((unsigned char) ptr[3])) {
1.1       cgd       255:            forLevel++;
                    256:            if (DEBUG(FOR))
1.23      dsl       257:                (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
1.1       cgd       258:        }
                    259:     }
                    260:
1.32      dsl       261:     Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line);
                    262:     Buf_AddByte(accumFor.buf, (Byte)'\n');
                    263:     return 1;
1.1       cgd       264: }
                    265:
                    266: 
                    267: /*-
                    268:  *-----------------------------------------------------------------------
                    269:  * For_Run --
1.7       christos  270:  *     Run the for loop, imitating the actions of an include file
1.1       cgd       271:  *
                    272:  * Results:
                    273:  *     None.
                    274:  *
                    275:  * Side Effects:
                    276:  *     None.
                    277:  *
                    278:  *-----------------------------------------------------------------------
                    279:  */
                    280: void
1.16      enami     281: For_Run(int lineno)
1.1       cgd       282: {
1.2       jtc       283:     For arg;
1.38      dsl       284:     int i, len;
1.39    ! dsl       285:     unsigned int num_items;
        !           286:     char *for_body;
1.38      dsl       287:     char *var, *item;
1.39    ! dsl       288:     char *cp;
        !           289:     char *cmd_cp;
        !           290:     char *body_end;
        !           291:     char ch;
        !           292:     Buffer cmds;
        !           293:     int short_var;
1.7       christos  294:
                    295:     arg = accumFor;
1.38      dsl       296:     memset(&accumFor, 0, sizeof accumFor);
1.1       cgd       297:
1.39    ! dsl       298:     num_items = strlist_num(&arg.items);
        !           299:     if (num_items % strlist_num(&arg.vars))
1.33      dsl       300:        /* Error message already printed */
1.38      dsl       301:        goto out;
1.7       christos  302:
1.39    ! dsl       303:     short_var = 0;
        !           304:     STRLIST_FOREACH(var, &arg.vars, i) {
        !           305:        if (var[1] == 0) {
        !           306:            short_var = 1;
        !           307:            break;
1.10      mycroft   308:        }
1.7       christos  309:     }
                    310:
1.39    ! dsl       311:     /*
        !           312:      * Scan the for loop body and replace references to the loop variables
        !           313:      * with variable references that expand to the required text.
        !           314:      * Using variable expansions ensures that the .for loop can't generate
        !           315:      * syntax, and that the later parsing will still see a variable.
        !           316:      * We assume that the null variable will never be defined.
        !           317:      *
        !           318:      * The detection of substitions of the loop control variable is naive.
        !           319:      * Many of the modifiers use \ to escape $ (not $) so it is possible
        !           320:      * to contrive a makefile where an unwanted substitution happens.
        !           321:      *
        !           322:      * Each loop expansion is fed back into the parser as if it were an
        !           323:      * include file.  This means we have to generate the last iteration first.
        !           324:      */
        !           325:     while (num_items != 0) {
        !           326:        num_items -= strlist_num(&arg.vars);
        !           327:        for_body = (char *)Buf_GetAll(arg.buf, &len);
        !           328:        body_end = for_body + len;
        !           329:        cmds = Buf_Init(len + 256);
        !           330:        cmd_cp = for_body;
        !           331:        for (cp = for_body; (cp = strchr(cp, '$')) != NULL;) {
        !           332:            ch = *++cp;
        !           333:            if (ch == '(' || ch == '{') {
        !           334:                char ech = ch == '(' ? ')' : '}';
        !           335:                cp++;
        !           336:                /* Check variable name against the .for loop varoables */
        !           337:                STRLIST_FOREACH(var, &arg.vars, i) {
        !           338:                    len = strlen(var);
        !           339:                    if (memcmp(cp, var, len) != 0)
        !           340:                        continue;
        !           341:                    if (cp[len] != ':' && cp[len] != ech)
        !           342:                        continue;
        !           343:                    /* Found a variable match. Replace with ${:U<value> */
        !           344:                    Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp);
        !           345:                    Buf_AddBytes(cmds, 2, ":U");
        !           346:                    cp += len;
        !           347:                    cmd_cp = cp;
        !           348:                    item = strlist_str(&arg.items, num_items + i);
        !           349:                    Buf_AddBytes(cmds, strlen(item), item);
        !           350:                    break;
        !           351:                }
        !           352:                continue;
        !           353:            }
        !           354:            if (ch == 0)
        !           355:                break;
        !           356:            /* Probably a single character name, ignore $$ and stupid ones. */
        !           357:            if (!short_var || strchr("}):$", ch) != NULL) {
        !           358:                cp++;
        !           359:                continue;
        !           360:            }
        !           361:            STRLIST_FOREACH(var, &arg.vars, i) {
        !           362:                if (var[0] != ch || var[1] != 0)
        !           363:                    continue;
        !           364:                /* Found a variable match. Replace with ${:U<value>} */
        !           365:                Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp);
        !           366:                Buf_AddBytes(cmds, 3, "{:U");
        !           367:                cmd_cp = ++cp;
        !           368:                item = strlist_str(&arg.items, num_items + i);
        !           369:                Buf_AddBytes(cmds, strlen(item), item);
        !           370:                Buf_AddBytes(cmds, 1, "}");
        !           371:                break;
        !           372:            }
        !           373:        }
        !           374:        Buf_AddBytes(cmds, body_end - cmd_cp, cmd_cp);
        !           375:
        !           376:        cp = Buf_GetAll(cmds, NULL);
        !           377:        if (DEBUG(FOR))
        !           378:            (void)fprintf(debug_file, "For: loop body:\n%s", cp);
        !           379:        Parse_SetInput(NULL, lineno, -1, cp);
        !           380:        Buf_Destroy(cmds, FALSE);
        !           381:     }
1.7       christos  382:
1.38      dsl       383:   out:
                    384:     strlist_clean(&arg.vars);
                    385:     strlist_clean(&arg.items);
1.1       cgd       386:
                    387:     Buf_Destroy(arg.buf, TRUE);
                    388: }

CVSweb <webmaster@jp.NetBSD.org>