Sun Mar 14 15:43:31 2021 UTC ()
make: separate parsing from evaluating for several modifiers

This aligns the implementation of these modifiers with the requirements
in the long comment starting with 'The ApplyModifier functions'.

No functional change.


(rillig)
diff -r1.863 -r1.864 src/usr.bin/make/var.c

cvs diff -r1.863 -r1.864 src/usr.bin/make/var.c (expand / switch to unified diff)

--- src/usr.bin/make/var.c 2021/03/14 15:24:37 1.863
+++ src/usr.bin/make/var.c 2021/03/14 15:43:31 1.864
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: var.c,v 1.863 2021/03/14 15:24:37 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.864 2021/03/14 15:43:31 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990, 1993 4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor. 8 * Adam de Boor.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -130,27 +130,27 @@ @@ -130,27 +130,27 @@
130#include <regex.h> 130#include <regex.h>
131#endif 131#endif
132#include <errno.h> 132#include <errno.h>
133#include <inttypes.h> 133#include <inttypes.h>
134#include <limits.h> 134#include <limits.h>
135#include <time.h> 135#include <time.h>
136 136
137#include "make.h" 137#include "make.h"
138#include "dir.h" 138#include "dir.h"
139#include "job.h" 139#include "job.h"
140#include "metachar.h" 140#include "metachar.h"
141 141
142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ 142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
143MAKE_RCSID("$NetBSD: var.c,v 1.863 2021/03/14 15:24:37 rillig Exp $"); 143MAKE_RCSID("$NetBSD: var.c,v 1.864 2021/03/14 15:43:31 rillig Exp $");
144 144
145typedef enum VarFlags { 145typedef enum VarFlags {
146 VFL_NONE = 0, 146 VFL_NONE = 0,
147 147
148 /* 148 /*
149 * The variable's value is currently being used by Var_Parse or 149 * The variable's value is currently being used by Var_Parse or
150 * Var_Subst. This marker is used to avoid endless recursion. 150 * Var_Subst. This marker is used to avoid endless recursion.
151 */ 151 */
152 VFL_IN_USE = 1 << 0, 152 VFL_IN_USE = 1 << 0,
153 153
154 /* 154 /*
155 * The variable comes from the environment. 155 * The variable comes from the environment.
156 * These variables are not registered in any GNode, therefore they 156 * These variables are not registered in any GNode, therefore they
@@ -2496,29 +2496,32 @@ ApplyModifier_Defined(const char **pp, A @@ -2496,29 +2496,32 @@ ApplyModifier_Defined(const char **pp, A
2496 if (eflags & VARE_WANTRES) 2496 if (eflags & VARE_WANTRES)
2497 Expr_SetValueOwn(expr, Buf_DoneData(&buf)); 2497 Expr_SetValueOwn(expr, Buf_DoneData(&buf));
2498 else 2498 else
2499 Buf_Done(&buf); 2499 Buf_Done(&buf);
2500 2500
2501 return AMR_OK; 2501 return AMR_OK;
2502} 2502}
2503 2503
2504/* :L */ 2504/* :L */
2505static ApplyModifierResult 2505static ApplyModifierResult
2506ApplyModifier_Literal(const char **pp, ApplyModifiersState *st) 2506ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
2507{ 2507{
2508 Expr *expr = st->expr; 2508 Expr *expr = st->expr;
 2509
 2510 (*pp)++;
 2511
2509 Expr_Define(expr); 2512 Expr_Define(expr);
2510 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str)); 2513 Expr_SetValueOwn(expr, bmake_strdup(expr->var->name.str));
2511 (*pp)++; 2514
2512 return AMR_OK; 2515 return AMR_OK;
2513} 2516}
2514 2517
2515static Boolean 2518static Boolean
2516TryParseTime(const char **pp, time_t *out_time) 2519TryParseTime(const char **pp, time_t *out_time)
2517{ 2520{
2518 char *end; 2521 char *end;
2519 unsigned long n; 2522 unsigned long n;
2520 2523
2521 if (!ch_isdigit(**pp)) 2524 if (!ch_isdigit(**pp))
2522 return FALSE; 2525 return FALSE;
2523 2526
2524 errno = 0; 2527 errno = 0;
@@ -2543,121 +2546,127 @@ ApplyModifier_Gmtime(const char **pp, Ap @@ -2543,121 +2546,127 @@ ApplyModifier_Gmtime(const char **pp, Ap
2543 2546
2544 if (mod[6] == '=') { 2547 if (mod[6] == '=') {
2545 const char *p = mod + 7; 2548 const char *p = mod + 7;
2546 if (!TryParseTime(&p, &utc)) { 2549 if (!TryParseTime(&p, &utc)) {
2547 Parse_Error(PARSE_FATAL, 2550 Parse_Error(PARSE_FATAL,
2548 "Invalid time value: %s", mod + 7); 2551 "Invalid time value: %s", mod + 7);
2549 return AMR_CLEANUP; 2552 return AMR_CLEANUP;
2550 } 2553 }
2551 *pp = p; 2554 *pp = p;
2552 } else { 2555 } else {
2553 utc = 0; 2556 utc = 0;
2554 *pp = mod + 6; 2557 *pp = mod + 6;
2555 } 2558 }
 2559
2556 Expr_SetValueOwn(st->expr, 2560 Expr_SetValueOwn(st->expr,
2557 VarStrftime(st->expr->value.str, TRUE, utc)); 2561 VarStrftime(st->expr->value.str, TRUE, utc));
 2562
2558 return AMR_OK; 2563 return AMR_OK;
2559} 2564}
2560 2565
2561/* :localtime */ 2566/* :localtime */
2562static ApplyModifierResult 2567static ApplyModifierResult
2563ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st) 2568ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st)
2564{ 2569{
2565 time_t utc; 2570 time_t utc;
2566 2571
2567 const char *mod = *pp; 2572 const char *mod = *pp;
2568 if (!ModMatchEq(mod, "localtime", st)) 2573 if (!ModMatchEq(mod, "localtime", st))
2569 return AMR_UNKNOWN; 2574 return AMR_UNKNOWN;
2570 2575
2571 if (mod[9] == '=') { 2576 if (mod[9] == '=') {
2572 const char *p = mod + 10; 2577 const char *p = mod + 10;
2573 if (!TryParseTime(&p, &utc)) { 2578 if (!TryParseTime(&p, &utc)) {
2574 Parse_Error(PARSE_FATAL, 2579 Parse_Error(PARSE_FATAL,
2575 "Invalid time value: %s", mod + 10); 2580 "Invalid time value: %s", mod + 10);
2576 return AMR_CLEANUP; 2581 return AMR_CLEANUP;
2577 } 2582 }
2578 *pp = p; 2583 *pp = p;
2579 } else { 2584 } else {
2580 utc = 0; 2585 utc = 0;
2581 *pp = mod + 9; 2586 *pp = mod + 9;
2582 } 2587 }
 2588
2583 Expr_SetValueOwn(st->expr, 2589 Expr_SetValueOwn(st->expr,
2584 VarStrftime(st->expr->value.str, FALSE, utc)); 2590 VarStrftime(st->expr->value.str, FALSE, utc));
 2591
2585 return AMR_OK; 2592 return AMR_OK;
2586} 2593}
2587 2594
2588/* :hash */ 2595/* :hash */
2589static ApplyModifierResult 2596static ApplyModifierResult
2590ApplyModifier_Hash(const char **pp, ApplyModifiersState *st) 2597ApplyModifier_Hash(const char **pp, ApplyModifiersState *st)
2591{ 2598{
2592 if (!ModMatch(*pp, "hash", st)) 2599 if (!ModMatch(*pp, "hash", st))
2593 return AMR_UNKNOWN; 2600 return AMR_UNKNOWN;
 2601 *pp += 4;
2594 2602
2595 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str)); 2603 Expr_SetValueOwn(st->expr, VarHash(st->expr->value.str));
2596 *pp += 4; 2604
2597 return AMR_OK; 2605 return AMR_OK;
2598} 2606}
2599 2607
2600/* :P */ 2608/* :P */
2601static ApplyModifierResult 2609static ApplyModifierResult
2602ApplyModifier_Path(const char **pp, ApplyModifiersState *st) 2610ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
2603{ 2611{
2604 Expr *expr = st->expr; 2612 Expr *expr = st->expr;
2605 GNode *gn; 2613 GNode *gn;
2606 char *path; 2614 char *path;
2607 2615
 2616 (*pp)++;
 2617
2608 Expr_Define(expr); 2618 Expr_Define(expr);
2609 2619
2610 gn = Targ_FindNode(expr->var->name.str); 2620 gn = Targ_FindNode(expr->var->name.str);
2611 if (gn == NULL || gn->type & OP_NOPATH) { 2621 if (gn == NULL || gn->type & OP_NOPATH) {
2612 path = NULL; 2622 path = NULL;
2613 } else if (gn->path != NULL) { 2623 } else if (gn->path != NULL) {
2614 path = bmake_strdup(gn->path); 2624 path = bmake_strdup(gn->path);
2615 } else { 2625 } else {
2616 SearchPath *searchPath = Suff_FindPath(gn); 2626 SearchPath *searchPath = Suff_FindPath(gn);
2617 path = Dir_FindFile(expr->var->name.str, searchPath); 2627 path = Dir_FindFile(expr->var->name.str, searchPath);
2618 } 2628 }
2619 if (path == NULL) 2629 if (path == NULL)
2620 path = bmake_strdup(expr->var->name.str); 2630 path = bmake_strdup(expr->var->name.str);
2621 Expr_SetValueOwn(expr, path); 2631 Expr_SetValueOwn(expr, path);
2622 2632
2623 (*pp)++; 
2624 return AMR_OK; 2633 return AMR_OK;
2625} 2634}
2626 2635
2627/* :!cmd! */ 2636/* :!cmd! */
2628static ApplyModifierResult 2637static ApplyModifierResult
2629ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st) 2638ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
2630{ 2639{
2631 Expr *expr = st->expr; 2640 Expr *expr = st->expr;
2632 char *cmd; 2641 char *cmd;
2633 const char *errfmt; 2642 const char *errfmt;
2634 VarParseResult res; 2643 VarParseResult res;
2635 2644
2636 (*pp)++; 2645 (*pp)++;
2637 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd); 2646 res = ParseModifierPart(pp, '!', expr->eflags, st, &cmd);
2638 if (res != VPR_OK) 2647 if (res != VPR_OK)
2639 return AMR_CLEANUP; 2648 return AMR_CLEANUP;
2640 2649
2641 errfmt = NULL; 2650 errfmt = NULL;
2642 if (expr->eflags & VARE_WANTRES) 2651 if (expr->eflags & VARE_WANTRES)
2643 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt)); 2652 Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt));
2644 else 2653 else
2645 Expr_SetValueRefer(expr, ""); 2654 Expr_SetValueRefer(expr, "");
2646 if (errfmt != NULL) 2655 if (errfmt != NULL)
2647 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */ 2656 Error(errfmt, cmd); /* XXX: why still return AMR_OK? */
2648 free(cmd); 2657 free(cmd);
2649 
2650 Expr_Define(expr); 2658 Expr_Define(expr);
 2659
2651 return AMR_OK; 2660 return AMR_OK;
2652} 2661}
2653 2662
2654/* 2663/*
2655 * The :range modifier generates an integer sequence as long as the words. 2664 * The :range modifier generates an integer sequence as long as the words.
2656 * The :range=7 modifier generates an integer sequence from 1 to 7. 2665 * The :range=7 modifier generates an integer sequence from 1 to 7.
2657 */ 2666 */
2658static ApplyModifierResult 2667static ApplyModifierResult
2659ApplyModifier_Range(const char **pp, ApplyModifiersState *st) 2668ApplyModifier_Range(const char **pp, ApplyModifiersState *st)
2660{ 2669{
2661 size_t n; 2670 size_t n;
2662 Buffer buf; 2671 Buffer buf;
2663 size_t i; 2672 size_t i;
@@ -2939,55 +2948,55 @@ static void @@ -2939,55 +2948,55 @@ static void
2939ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) 2948ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
2940{ 2949{
2941 SepBuf_AddStr(buf, word); 2950 SepBuf_AddStr(buf, word);
2942} 2951}
2943 2952
2944/* :ts<separator> */ 2953/* :ts<separator> */
2945static ApplyModifierResult 2954static ApplyModifierResult
2946ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st) 2955ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st)
2947{ 2956{
2948 const char *sep = *pp + 2; 2957 const char *sep = *pp + 2;
2949 2958
2950 /* ":ts<any><endc>" or ":ts<any>:" */ 2959 /* ":ts<any><endc>" or ":ts<any>:" */
2951 if (sep[0] != st->endc && IsDelimiter(sep[1], st)) { 2960 if (sep[0] != st->endc && IsDelimiter(sep[1], st)) {
2952 st->sep = sep[0]; 
2953 *pp = sep + 1; 2961 *pp = sep + 1;
 2962 st->sep = sep[0];
2954 goto ok; 2963 goto ok;
2955 } 2964 }
2956 2965
2957 /* ":ts<endc>" or ":ts:" */ 2966 /* ":ts<endc>" or ":ts:" */
2958 if (IsDelimiter(sep[0], st)) { 2967 if (IsDelimiter(sep[0], st)) {
2959 st->sep = '\0'; /* no separator */ 
2960 *pp = sep; 2968 *pp = sep;
 2969 st->sep = '\0'; /* no separator */
2961 goto ok; 2970 goto ok;
2962 } 2971 }
2963 2972
2964 /* ":ts<unrecognised><unrecognised>". */ 2973 /* ":ts<unrecognised><unrecognised>". */
2965 if (sep[0] != '\\') { 2974 if (sep[0] != '\\') {
2966 (*pp)++; /* just for backwards compatibility */ 2975 (*pp)++; /* just for backwards compatibility */
2967 return AMR_BAD; 2976 return AMR_BAD;
2968 } 2977 }
2969 2978
2970 /* ":ts\n" */ 2979 /* ":ts\n" */
2971 if (sep[1] == 'n') { 2980 if (sep[1] == 'n') {
2972 st->sep = '\n'; 
2973 *pp = sep + 2; 2981 *pp = sep + 2;
 2982 st->sep = '\n';
2974 goto ok; 2983 goto ok;
2975 } 2984 }
2976 2985
2977 /* ":ts\t" */ 2986 /* ":ts\t" */
2978 if (sep[1] == 't') { 2987 if (sep[1] == 't') {
2979 st->sep = '\t'; 
2980 *pp = sep + 2; 2988 *pp = sep + 2;
 2989 st->sep = '\t';
2981 goto ok; 2990 goto ok;
2982 } 2991 }
2983 2992
2984 /* ":ts\x40" or ":ts\100" */ 2993 /* ":ts\x40" or ":ts\100" */
2985 { 2994 {
2986 const char *p = sep + 1; 2995 const char *p = sep + 1;
2987 int base = 8; /* assume octal */ 2996 int base = 8; /* assume octal */
2988 2997
2989 if (sep[1] == 'x') { 2998 if (sep[1] == 'x') {
2990 base = 16; 2999 base = 16;
2991 p++; 3000 p++;
2992 } else if (!ch_isdigit(sep[1])) { 3001 } else if (!ch_isdigit(sep[1])) {
2993 (*pp)++; /* just for backwards compatibility */ 3002 (*pp)++; /* just for backwards compatibility */
@@ -3052,51 +3061,51 @@ ApplyModifier_To(const char **pp, ApplyM @@ -3052,51 +3061,51 @@ ApplyModifier_To(const char **pp, ApplyM
3052 *pp = mod + 1; 3061 *pp = mod + 1;
3053 return AMR_BAD; /* Found ":t<endc>" or ":t:". */ 3062 return AMR_BAD; /* Found ":t<endc>" or ":t:". */
3054 } 3063 }
3055 3064
3056 if (mod[1] == 's') 3065 if (mod[1] == 's')
3057 return ApplyModifier_ToSep(pp, st); 3066 return ApplyModifier_ToSep(pp, st);
3058 3067
3059 if (!IsDelimiter(mod[2], st)) { /* :t<unrecognized> */ 3068 if (!IsDelimiter(mod[2], st)) { /* :t<unrecognized> */
3060 *pp = mod + 1; 3069 *pp = mod + 1;
3061 return AMR_BAD; 3070 return AMR_BAD;
3062 } 3071 }
3063 3072
3064 if (mod[1] == 'A') { /* :tA */ 3073 if (mod[1] == 'A') { /* :tA */
3065 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord); 
3066 *pp = mod + 2; 3074 *pp = mod + 2;
 3075 ModifyWords(st, ModifyWord_Realpath, NULL, st->oneBigWord);
3067 return AMR_OK; 3076 return AMR_OK;
3068 } 3077 }
3069 3078
3070 if (mod[1] == 'u') { /* :tu */ 3079 if (mod[1] == 'u') { /* :tu */
3071 Expr_SetValueOwn(expr, str_toupper(expr->value.str)); 
3072 *pp = mod + 2; 3080 *pp = mod + 2;
 3081 Expr_SetValueOwn(expr, str_toupper(expr->value.str));
3073 return AMR_OK; 3082 return AMR_OK;
3074 } 3083 }
3075 3084
3076 if (mod[1] == 'l') { /* :tl */ 3085 if (mod[1] == 'l') { /* :tl */
3077 Expr_SetValueOwn(expr, str_tolower(expr->value.str)); 
3078 *pp = mod + 2; 3086 *pp = mod + 2;
 3087 Expr_SetValueOwn(expr, str_tolower(expr->value.str));
3079 return AMR_OK; 3088 return AMR_OK;
3080 } 3089 }
3081 3090
3082 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */ 3091 if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
3083 st->oneBigWord = mod[1] == 'W'; 
3084 *pp = mod + 2; 3092 *pp = mod + 2;
 3093 st->oneBigWord = mod[1] == 'W';
3085 return AMR_OK; 3094 return AMR_OK;
3086 } 3095 }
3087 3096
3088 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ 3097 /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
3089 *pp = mod + 1; 3098 *pp = mod + 1; /* XXX: unnecessary but observable */
3090 return AMR_BAD; 3099 return AMR_BAD;
3091} 3100}
3092 3101
3093/* :[#], :[1], :[-1..1], etc. */ 3102/* :[#], :[1], :[-1..1], etc. */
3094static ApplyModifierResult 3103static ApplyModifierResult
3095ApplyModifier_Words(const char **pp, ApplyModifiersState *st) 3104ApplyModifier_Words(const char **pp, ApplyModifiersState *st)
3096{ 3105{
3097 Expr *expr = st->expr; 3106 Expr *expr = st->expr;
3098 char *estr; 3107 char *estr;
3099 int first, last; 3108 int first, last;
3100 VarParseResult res; 3109 VarParseResult res;
3101 const char *p; 3110 const char *p;
3102 3111
@@ -3206,26 +3215,28 @@ ShuffleStrings(char **strs, size_t n) @@ -3206,26 +3215,28 @@ ShuffleStrings(char **strs, size_t n)
3206 size_t rndidx = (size_t)random() % (i + 1); 3215 size_t rndidx = (size_t)random() % (i + 1);
3207 char *t = strs[i]; 3216 char *t = strs[i];
3208 strs[i] = strs[rndidx]; 3217 strs[i] = strs[rndidx];
3209 strs[rndidx] = t; 3218 strs[rndidx] = t;
3210 } 3219 }
3211} 3220}
3212 3221
3213/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ 3222/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
3214static ApplyModifierResult 3223static ApplyModifierResult
3215ApplyModifier_Order(const char **pp, ApplyModifiersState *st) 3224ApplyModifier_Order(const char **pp, ApplyModifiersState *st)
3216{ 3225{
3217 const char *mod = (*pp)++; /* skip past the 'O' in any case */ 3226 const char *mod = (*pp)++; /* skip past the 'O' in any case */
3218 3227
 3228 /* TODO: separate parsing from evaluating */
 3229
3219 Words words = Str_Words(st->expr->value.str, FALSE); 3230 Words words = Str_Words(st->expr->value.str, FALSE);
3220 3231
3221 if (IsDelimiter(mod[1], st)) { 3232 if (IsDelimiter(mod[1], st)) {
3222 /* :O sorts ascending */ 3233 /* :O sorts ascending */
3223 qsort(words.words, words.len, sizeof words.words[0], 3234 qsort(words.words, words.len, sizeof words.words[0],
3224 str_cmp_asc); 3235 str_cmp_asc);
3225 3236
3226 } else if ((mod[1] == 'r' || mod[1] == 'x') && 3237 } else if ((mod[1] == 'r' || mod[1] == 'x') &&
3227 IsDelimiter(mod[2], st)) { 3238 IsDelimiter(mod[2], st)) {
3228 (*pp)++; 3239 (*pp)++;
3229 3240
3230 if (mod[1] == 'r') { /* :Or sorts descending */ 3241 if (mod[1] == 'r') { /* :Or sorts descending */
3231 qsort(words.words, words.len, sizeof words.words[0], 3242 qsort(words.words, words.len, sizeof words.words[0],
@@ -3311,26 +3322,28 @@ ApplyModifier_IfElse(const char **pp, Ap @@ -3311,26 +3322,28 @@ ApplyModifier_IfElse(const char **pp, Ap
3311 * it was not already set. 3322 * it was not already set.
3312 * ::+=<str> Appends <str> to variable. 3323 * ::+=<str> Appends <str> to variable.
3313 * ::!=<cmd> Assigns output of <cmd> as the new value of 3324 * ::!=<cmd> Assigns output of <cmd> as the new value of
3314 * variable. 3325 * variable.
3315 */ 3326 */
3316static ApplyModifierResult 3327static ApplyModifierResult
3317ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) 3328ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
3318{ 3329{
3319 Expr *expr = st->expr; 3330 Expr *expr = st->expr;
3320 GNode *scope; 3331 GNode *scope;
3321 char *val; 3332 char *val;
3322 VarParseResult res; 3333 VarParseResult res;
3323 3334
 3335 /* TODO: separate parsing from evaluating */
 3336
3324 const char *mod = *pp; 3337 const char *mod = *pp;
3325 const char *op = mod + 1; 3338 const char *op = mod + 1;
3326 3339
3327 if (op[0] == '=') 3340 if (op[0] == '=')
3328 goto ok; 3341 goto ok;
3329 if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=') 3342 if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=')
3330 goto ok; 3343 goto ok;
3331 return AMR_UNKNOWN; /* "::<unrecognised>" */ 3344 return AMR_UNKNOWN; /* "::<unrecognised>" */
3332 3345
3333ok: 3346ok:
3334 if (expr->var->name.str[0] == '\0') { 3347 if (expr->var->name.str[0] == '\0') {
3335 *pp = mod + 1; 3348 *pp = mod + 1;
3336 return AMR_BAD; 3349 return AMR_BAD;
@@ -3403,49 +3416,56 @@ ApplyModifier_Remember(const char **pp,  @@ -3403,49 +3416,56 @@ ApplyModifier_Remember(const char **pp,
3403 Expr *expr = st->expr; 3416 Expr *expr = st->expr;
3404 const char *mod = *pp; 3417 const char *mod = *pp;
3405 if (!ModMatchEq(mod, "_", st)) 3418 if (!ModMatchEq(mod, "_", st))
3406 return AMR_UNKNOWN; 3419 return AMR_UNKNOWN;
3407 3420
3408 if (mod[1] == '=') { 3421 if (mod[1] == '=') {
3409 /* 3422 /*
3410 * XXX: This ad-hoc call to strcspn deviates from the usual 3423 * XXX: This ad-hoc call to strcspn deviates from the usual
3411 * behavior defined in ParseModifierPart. This creates an 3424 * behavior defined in ParseModifierPart. This creates an
3412 * unnecessary, undocumented inconsistency in make. 3425 * unnecessary, undocumented inconsistency in make.
3413 */ 3426 */
3414 size_t n = strcspn(mod + 2, ":)}"); 3427 size_t n = strcspn(mod + 2, ":)}");
3415 char *name = bmake_strldup(mod + 2, n); 3428 char *name = bmake_strldup(mod + 2, n);
 3429 *pp = mod + 2 + n;
 3430
 3431 /*
 3432 * FIXME: do not expand the variable name here; it would only
 3433 * work for single-character variable names anyway.
 3434 */
3416 Var_SetExpand(expr->scope, name, expr->value.str); 3435 Var_SetExpand(expr->scope, name, expr->value.str);
3417 free(name); 3436 free(name);
3418 *pp = mod + 2 + n; 
3419 } else { 3437 } else {
3420 Var_Set(expr->scope, "_", expr->value.str); 
3421 *pp = mod + 1; 3438 *pp = mod + 1;
 3439
 3440 Var_Set(expr->scope, "_", expr->value.str);
3422 } 3441 }
3423 return AMR_OK; 3442 return AMR_OK;
3424} 3443}
3425 3444
3426/* 3445/*
3427 * Apply the given function to each word of the variable value, 3446 * Apply the given function to each word of the variable value,
3428 * for a single-letter modifier such as :H, :T. 3447 * for a single-letter modifier such as :H, :T.
3429 */ 3448 */
3430static ApplyModifierResult 3449static ApplyModifierResult
3431ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st, 3450ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st,
3432 ModifyWordProc modifyWord) 3451 ModifyWordProc modifyWord)
3433{ 3452{
3434 if (!IsDelimiter((*pp)[1], st)) 3453 if (!IsDelimiter((*pp)[1], st))
3435 return AMR_UNKNOWN; 3454 return AMR_UNKNOWN;
 3455 (*pp)++;
3436 3456
3437 ModifyWords(st, modifyWord, NULL, st->oneBigWord); 3457 ModifyWords(st, modifyWord, NULL, st->oneBigWord);
3438 (*pp)++; 3458
3439 return AMR_OK; 3459 return AMR_OK;
3440} 3460}
3441 3461
3442static ApplyModifierResult 3462static ApplyModifierResult
3443ApplyModifier_Unique(const char **pp, ApplyModifiersState *st) 3463ApplyModifier_Unique(const char **pp, ApplyModifiersState *st)
3444{ 3464{
3445 if (!IsDelimiter((*pp)[1], st)) 3465 if (!IsDelimiter((*pp)[1], st))
3446 return AMR_UNKNOWN; 3466 return AMR_UNKNOWN;
3447 (*pp)++; 3467 (*pp)++;
3448 3468
3449 Expr_SetValueOwn(st->expr, VarUniq(st->expr->value.str)); 3469 Expr_SetValueOwn(st->expr, VarUniq(st->expr->value.str));
3450 3470
3451 return AMR_OK; 3471 return AMR_OK;
@@ -3507,44 +3527,45 @@ ApplyModifier_SysV(const char **pp, Appl @@ -3507,44 +3527,45 @@ ApplyModifier_SysV(const char **pp, Appl
3507 return AMR_OK; 3527 return AMR_OK;
3508} 3528}
3509#endif 3529#endif
3510 3530
3511#ifdef SUNSHCMD 3531#ifdef SUNSHCMD
3512/* :sh */ 3532/* :sh */
3513static ApplyModifierResult 3533static ApplyModifierResult
3514ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st) 3534ApplyModifier_SunShell(const char **pp, ApplyModifiersState *st)
3515{ 3535{
3516 Expr *expr = st->expr; 3536 Expr *expr = st->expr;
3517 const char *p = *pp; 3537 const char *p = *pp;
3518 if (!(p[1] == 'h' && IsDelimiter(p[2], st))) 3538 if (!(p[1] == 'h' && IsDelimiter(p[2], st)))
3519 return AMR_UNKNOWN; 3539 return AMR_UNKNOWN;
 3540 *pp = p + 2;
3520 3541
3521 if (expr->eflags & VARE_WANTRES) { 3542 if (expr->eflags & VARE_WANTRES) {
3522 const char *errfmt; 3543 const char *errfmt;
3523 char *output = Cmd_Exec(expr->value.str, &errfmt); 3544 char *output = Cmd_Exec(expr->value.str, &errfmt);
3524 if (errfmt != NULL) 3545 if (errfmt != NULL)
3525 Error(errfmt, expr->value.str); 3546 Error(errfmt, expr->value.str);
3526 Expr_SetValueOwn(expr, output); 3547 Expr_SetValueOwn(expr, output);
3527 } else { 3548 } else {
3528 /* 3549 /*
3529 * TODO: Check whether returning ":sh" would be 3550 * TODO: Check whether returning ":sh" would be
3530 * more consistent with the other modifiers. 3551 * more consistent with the other modifiers.
3531 * 3552 *
3532 * TODO: Add a unit test demonstrating that the 3553 * TODO: Add a unit test demonstrating that the
3533 * actual value of this expression has any effect. 3554 * actual value of this expression has any effect.
3534 */ 3555 */
3535 Expr_SetValueRefer(expr, ""); 3556 Expr_SetValueRefer(expr, "");
3536 } 3557 }
3537 *pp = p + 2; 3558
3538 return AMR_OK; 3559 return AMR_OK;
3539} 3560}
3540#endif 3561#endif
3541 3562
3542static void 3563static void
3543LogBeforeApply(const ApplyModifiersState *st, const char *mod) 3564LogBeforeApply(const ApplyModifiersState *st, const char *mod)
3544{ 3565{
3545 const Expr *expr = st->expr; 3566 const Expr *expr = st->expr;
3546 char eflags_str[VarEvalFlags_ToStringSize]; 3567 char eflags_str[VarEvalFlags_ToStringSize];
3547 char vflags_str[VarFlags_ToStringSize]; 3568 char vflags_str[VarFlags_ToStringSize];
3548 Boolean is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], st); 3569 Boolean is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], st);
3549 3570
3550 /* At this point, only the first character of the modifier can 3571 /* At this point, only the first character of the modifier can