|
|
1.1 root 1: /*
2: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3: * Copyright (c) 1988, 1989 by Adam de Boor
4: * Copyright (c) 1989 by Berkeley Softworks
5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Adam de Boor.
9: *
10: * Redistribution and use in source and binary forms are permitted
11: * provided that: (1) source distributions retain this entire copyright
12: * notice and comment, and (2) distributions including binaries display
13: * the following acknowledgement: ``This product includes software
14: * developed by the University of California, Berkeley and its contributors''
15: * in the documentation or other materials provided with the distribution
16: * and in all advertising materials mentioning features or use of this
17: * software. Neither the name of the University nor the names of its
18: * contributors may be used to endorse or promote products derived
19: * from this software without specific prior written permission.
20: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23: */
24:
25: #ifndef lint
26: static char sccsid[] = "@(#)var.c 5.7 (Berkeley) 6/1/90";
27: #endif /* not lint */
28:
29: /*-
30: * var.c --
31: * Variable-handling functions
32: *
33: * Interface:
34: * Var_Set Set the value of a variable in the given
35: * context. The variable is created if it doesn't
36: * yet exist. The value and variable name need not
37: * be preserved.
38: *
39: * Var_Append Append more characters to an existing variable
40: * in the given context. The variable needn't
41: * exist already -- it will be created if it doesn't.
42: * A space is placed between the old value and the
43: * new one.
44: *
45: * Var_Exists See if a variable exists.
46: *
47: * Var_Value Return the value of a variable in a context or
48: * NULL if the variable is undefined.
49: *
50: * Var_Subst Substitute for all variables in a string using
51: * the given context as the top-most one. If the
52: * third argument is non-zero, Parse_Error is
53: * called if any variables are undefined.
54: *
55: * Var_Parse Parse a variable expansion from a string and
56: * return the result and the number of characters
57: * consumed.
58: *
59: * Var_Delete Delete a variable in a context.
60: *
61: * Var_Init Initialize this module.
62: *
63: * Debugging:
64: * Var_Dump Print out all variables defined in the given
65: * context.
66: *
67: * XXX: There's a lot of duplication in these functions.
68: */
69:
70: #include <ctype.h>
71: #include "make.h"
72: #include "buf.h"
73: extern char *getenv();
74:
75: /*
76: * This is a harmless return value for Var_Parse that can be used by Var_Subst
77: * to determine if there was an error in parsing -- easier than returning
78: * a flag, as things outside this module don't give a hoot.
79: */
80: char var_Error[] = "";
81:
82: /*
83: * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
84: * set false. Why not just use a constant? Well, gcc likes to condense
85: * identical string instances...
86: */
87: char varNoError[] = "";
88:
89: /*
90: * Internally, variables are contained in four different contexts.
91: * 1) the environment. They may not be changed. If an environment
92: * variable is appended-to, the result is placed in the global
93: * context.
94: * 2) the global context. Variables set in the Makefile are located in
95: * the global context. It is the penultimate context searched when
96: * substituting.
97: * 3) the command-line context. All variables set on the command line
98: * are placed in this context. They are UNALTERABLE once placed here.
99: * 4) the local context. Each target has associated with it a context
100: * list. On this list are located the structures describing such
101: * local variables as $(@) and $(*)
102: * The four contexts are searched in the reverse order from which they are
103: * listed.
104: */
105: GNode *VAR_GLOBAL; /* variables from the makefile */
106: GNode *VAR_CMD; /* variables defined on the command-line */
107:
108: #define FIND_CMD 0x1 /* look in VAR_CMD when searching */
109: #define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */
110: #define FIND_ENV 0x4 /* look in the environment also */
111:
112: typedef struct Var {
113: char *name; /* the variable's name */
114: Buffer val; /* its value */
115: int flags; /* miscellaneous status flags */
116: #define VAR_IN_USE 1 /* Variable's value currently being used.
117: * Used to avoid recursion */
118: #define VAR_FROM_ENV 2 /* Variable comes from the environment */
119: #define VAR_JUNK 4 /* Variable is a junk variable that
120: * should be destroyed when done with
121: * it. Used by Var_Parse for undefined,
122: * modified variables */
123: } Var;
124:
125: /*-
126: *-----------------------------------------------------------------------
127: * VarCmp --
128: * See if the given variable matches the named one. Called from
129: * Lst_Find when searching for a variable of a given name.
130: *
131: * Results:
132: * 0 if they match. non-zero otherwise.
133: *
134: * Side Effects:
135: * none
136: *-----------------------------------------------------------------------
137: */
138: static int
139: VarCmp (v, name)
140: Var *v; /* VAR structure to compare */
141: char *name; /* name to look for */
142: {
143: return (strcmp (name, v->name));
144: }
145:
146: /*-
147: *-----------------------------------------------------------------------
148: * VarFind --
149: * Find the given variable in the given context and any other contexts
150: * indicated.
151: *
152: * Results:
153: * A pointer to the structure describing the desired variable or
154: * NIL if the variable does not exist.
155: *
156: * Side Effects:
157: * None
158: *-----------------------------------------------------------------------
159: */
160: static Var *
161: VarFind (name, ctxt, flags)
162: char *name; /* name to find */
163: GNode *ctxt; /* context in which to find it */
164: int flags; /* FIND_GLOBAL set means to look in the
165: * VAR_GLOBAL context as well.
166: * FIND_CMD set means to look in the VAR_CMD
167: * context also.
168: * FIND_ENV set means to look in the
169: * environment */
170: {
171: LstNode var;
172: Var *v;
173:
174: /*
175: * If the variable name begins with a '.', it could very well be one of
176: * the local ones. We check the name against all the local variables
177: * and substitute the short version in for 'name' if it matches one of
178: * them.
179: */
180: if (*name == '.' && isupper(name[1]))
181: switch (name[1]) {
182: case 'A':
183: if (!strcmp(name, ".ALLSRC"))
184: name = ALLSRC;
185: if (!strcmp(name, ".ARCHIVE"))
186: name = ARCHIVE;
187: break;
188: case 'I':
189: if (!strcmp(name, ".IMPSRC"))
190: name = IMPSRC;
191: break;
192: case 'M':
193: if (!strcmp(name, ".MEMBER"))
194: name = MEMBER;
195: break;
196: case 'O':
197: if (!strcmp(name, ".OODATE"))
198: name = OODATE;
199: break;
200: case 'P':
201: if (!strcmp(name, ".PREFIX"))
202: name = PREFIX;
203: break;
204: case 'T':
205: if (!strcmp(name, ".TARGET"))
206: name = TARGET;
207: break;
208: }
209: /*
210: * First look for the variable in the given context. If it's not there,
211: * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
212: * depending on the FIND_* flags in 'flags'
213: */
214: var = Lst_Find (ctxt->context, (ClientData)name, VarCmp);
215:
216: if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) {
217: var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp);
218: }
219: if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) &&
220: (ctxt != VAR_GLOBAL))
221: {
222: var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
223: }
224: if ((var == NILLNODE) && (flags & FIND_ENV)) {
225: char *env;
226:
227: if ((env = getenv (name)) != NULL) {
228: /*
229: * If the variable is found in the environment, we only duplicate
230: * its value (since eVarVal was allocated on the stack). The name
231: * doesn't need duplication since it's always in the environment
232: */
233: int len;
234:
235: v = (Var *) emalloc(sizeof(Var));
236: v->name = name;
237:
238: len = strlen(env);
239:
240: v->val = Buf_Init(len);
241: Buf_AddBytes(v->val, len, (Byte *)env);
242:
243: v->flags = VAR_FROM_ENV;
244: return (v);
245: } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
246: (ctxt != VAR_GLOBAL))
247: {
248: var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
249: if (var == NILLNODE) {
250: return ((Var *) NIL);
251: } else {
252: return ((Var *)Lst_Datum(var));
253: }
254: } else {
255: return((Var *)NIL);
256: }
257: } else if (var == NILLNODE) {
258: return ((Var *) NIL);
259: } else {
260: return ((Var *) Lst_Datum (var));
261: }
262: }
263:
264: /*-
265: *-----------------------------------------------------------------------
266: * VarAdd --
267: * Add a new variable of name name and value val to the given context
268: *
269: * Results:
270: * None
271: *
272: * Side Effects:
273: * The new variable is placed at the front of the given context
274: * The name and val arguments are duplicated so they may
275: * safely be freed.
276: *-----------------------------------------------------------------------
277: */
278: static
279: VarAdd (name, val, ctxt)
280: char *name; /* name of variable to add */
281: char *val; /* value to set it to */
282: GNode *ctxt; /* context in which to set it */
283: {
284: register Var *v;
285: int len;
286:
287: v = (Var *) emalloc (sizeof (Var));
288:
289: v->name = strdup (name);
290:
291: len = strlen(val);
292: v->val = Buf_Init(len+1);
293: Buf_AddBytes(v->val, len, (Byte *)val);
294:
295: v->flags = 0;
296:
297: (void) Lst_AtFront (ctxt->context, (ClientData)v);
298: if (DEBUG(VAR)) {
299: printf("%s:%s = %s\n", ctxt->name, name, val);
300: }
301: }
302:
303: /*-
304: *-----------------------------------------------------------------------
305: * Var_Delete --
306: * Remove a variable from a context.
307: *
308: * Results:
309: * None.
310: *
311: * Side Effects:
312: * The Var structure is removed and freed.
313: *
314: *-----------------------------------------------------------------------
315: */
316: void
317: Var_Delete(name, ctxt)
318: char *name;
319: GNode *ctxt;
320: {
321: LstNode ln;
322:
323: if (DEBUG(VAR)) {
324: printf("%s:delete %s\n", ctxt->name, name);
325: }
326: ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp);
327: if (ln != NILLNODE) {
328: register Var *v;
329:
330: v = (Var *)Lst_Datum(ln);
331: Lst_Remove(ctxt->context, ln);
332: Buf_Destroy(v->val, TRUE);
333: free(v->name);
334: free((char *)v);
335: }
336: }
337:
338: /*-
339: *-----------------------------------------------------------------------
340: * Var_Set --
341: * Set the variable name to the value val in the given context.
342: *
343: * Results:
344: * None.
345: *
346: * Side Effects:
347: * If the variable doesn't yet exist, a new record is created for it.
348: * Else the old value is freed and the new one stuck in its place
349: *
350: * Notes:
351: * The variable is searched for only in its context before being
352: * created in that context. I.e. if the context is VAR_GLOBAL,
353: * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
354: * VAR_CMD->context is searched. This is done to avoid the literally
355: * thousands of unnecessary strcmp's that used to be done to
356: * set, say, $(@) or $(<).
357: *-----------------------------------------------------------------------
358: */
359: void
360: Var_Set (name, val, ctxt)
361: char *name; /* name of variable to set */
362: char *val; /* value to give to the variable */
363: GNode *ctxt; /* context in which to set it */
364: {
365: register Var *v;
366:
367: /*
368: * We only look for a variable in the given context since anything set
369: * here will override anything in a lower context, so there's not much
370: * point in searching them all just to save a bit of memory...
371: */
372: v = VarFind (name, ctxt, 0);
373: if (v == (Var *) NIL) {
374: VarAdd (name, val, ctxt);
375: } else {
376: Buf_Discard(v->val, Buf_Size(v->val));
377: Buf_AddBytes(v->val, strlen(val), (Byte *)val);
378:
379: if (DEBUG(VAR)) {
380: printf("%s:%s = %s\n", ctxt->name, name, val);
381: }
382: }
383: /*
384: * Any variables given on the command line are automatically exported
385: * to the environment (as per POSIX standard)
386: */
387: if (ctxt == VAR_CMD) {
388: setenv(name, val);
389: }
390: }
391:
392: /*-
393: *-----------------------------------------------------------------------
394: * Var_Append --
395: * The variable of the given name has the given value appended to it in
396: * the given context.
397: *
398: * Results:
399: * None
400: *
401: * Side Effects:
402: * If the variable doesn't exist, it is created. Else the strings
403: * are concatenated (with a space in between).
404: *
405: * Notes:
406: * Only if the variable is being sought in the global context is the
407: * environment searched.
408: * XXX: Knows its calling circumstances in that if called with ctxt
409: * an actual target, it will only search that context since only
410: * a local variable could be being appended to. This is actually
411: * a big win and must be tolerated.
412: *-----------------------------------------------------------------------
413: */
414: void
415: Var_Append (name, val, ctxt)
416: char *name; /* Name of variable to modify */
417: char *val; /* String to append to it */
418: GNode *ctxt; /* Context in which this should occur */
419: {
420: register Var *v;
421: register char *cp;
422:
423: v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
424:
425: if (v == (Var *) NIL) {
426: VarAdd (name, val, ctxt);
427: } else {
428: Buf_AddByte(v->val, (Byte)' ');
429: Buf_AddBytes(v->val, strlen(val), (Byte *)val);
430:
431: if (DEBUG(VAR)) {
432: printf("%s:%s = %s\n", ctxt->name, name,
433: Buf_GetAll(v->val, (int *)NULL));
434: }
435:
436: if (v->flags & VAR_FROM_ENV) {
437: /*
438: * If the original variable came from the environment, we
439: * have to install it in the global context (we could place
440: * it in the environment, but then we should provide a way to
441: * export other variables...)
442: */
443: v->flags &= ~VAR_FROM_ENV;
444: Lst_AtFront(ctxt->context, (ClientData)v);
445: }
446: }
447: }
448:
449: /*-
450: *-----------------------------------------------------------------------
451: * Var_Exists --
452: * See if the given variable exists.
453: *
454: * Results:
455: * TRUE if it does, FALSE if it doesn't
456: *
457: * Side Effects:
458: * None.
459: *
460: *-----------------------------------------------------------------------
461: */
462: Boolean
463: Var_Exists(name, ctxt)
464: char *name; /* Variable to find */
465: GNode *ctxt; /* Context in which to start search */
466: {
467: Var *v;
468:
469: v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV);
470:
471: if (v == (Var *)NIL) {
472: return(FALSE);
473: } else if (v->flags & VAR_FROM_ENV) {
474: Buf_Destroy(v->val, TRUE);
475: free((char *)v);
476: }
477: return(TRUE);
478: }
479:
480: /*-
481: *-----------------------------------------------------------------------
482: * Var_Value --
483: * Return the value of the named variable in the given context
484: *
485: * Results:
486: * The value if the variable exists, NULL if it doesn't
487: *
488: * Side Effects:
489: * None
490: *-----------------------------------------------------------------------
491: */
492: char *
493: Var_Value (name, ctxt)
494: char *name; /* name to find */
495: GNode *ctxt; /* context in which to search for it */
496: {
497: Var *v;
498:
499: v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
500: if (v != (Var *) NIL) {
501: return ((char *)Buf_GetAll(v->val, (int *)NULL));
502: } else {
503: return ((char *) NULL);
504: }
505: }
506:
507: /*-
508: *-----------------------------------------------------------------------
509: * VarHead --
510: * Remove the tail of the given word and place the result in the given
511: * buffer.
512: *
513: * Results:
514: * TRUE if characters were added to the buffer (a space needs to be
515: * added to the buffer before the next word).
516: *
517: * Side Effects:
518: * The trimmed word is added to the buffer.
519: *
520: *-----------------------------------------------------------------------
521: */
522: static Boolean
523: VarHead (word, addSpace, buf)
524: char *word; /* Word to trim */
525: Boolean addSpace; /* True if need to add a space to the buffer
526: * before sticking in the head */
527: Buffer buf; /* Buffer in which to store it */
528: {
529: register char *slash;
530:
531: slash = rindex (word, '/');
532: if (slash != (char *)NULL) {
533: if (addSpace) {
534: Buf_AddByte (buf, (Byte)' ');
535: }
536: *slash = '\0';
537: Buf_AddBytes (buf, strlen (word), (Byte *)word);
538: *slash = '/';
539: return (TRUE);
540: } else {
541: /*
542: * If no directory part, give . (q.v. the POSIX standard)
543: */
544: if (addSpace) {
545: Buf_AddBytes(buf, 2, (Byte *)" .");
546: } else {
547: Buf_AddByte(buf, (Byte)'.');
548: }
549: return(TRUE);
550: }
551: }
552:
553: /*-
554: *-----------------------------------------------------------------------
555: * VarTail --
556: * Remove the head of the given word and place the result in the given
557: * buffer.
558: *
559: * Results:
560: * TRUE if characters were added to the buffer (a space needs to be
561: * added to the buffer before the next word).
562: *
563: * Side Effects:
564: * The trimmed word is added to the buffer.
565: *
566: *-----------------------------------------------------------------------
567: */
568: static Boolean
569: VarTail (word, addSpace, buf)
570: char *word; /* Word to trim */
571: Boolean addSpace; /* TRUE if need to stick a space in the
572: * buffer before adding the tail */
573: Buffer buf; /* Buffer in which to store it */
574: {
575: register char *slash;
576:
577: if (addSpace) {
578: Buf_AddByte (buf, (Byte)' ');
579: }
580:
581: slash = rindex (word, '/');
582: if (slash != (char *)NULL) {
583: *slash++ = '\0';
584: Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
585: slash[-1] = '/';
586: } else {
587: Buf_AddBytes (buf, strlen(word), (Byte *)word);
588: }
589: return (TRUE);
590: }
591:
592: /*-
593: *-----------------------------------------------------------------------
594: * VarSuffix --
595: * Place the suffix of the given word in the given buffer.
596: *
597: * Results:
598: * TRUE if characters were added to the buffer (a space needs to be
599: * added to the buffer before the next word).
600: *
601: * Side Effects:
602: * The suffix from the word is placed in the buffer.
603: *
604: *-----------------------------------------------------------------------
605: */
606: static Boolean
607: VarSuffix (word, addSpace, buf)
608: char *word; /* Word to trim */
609: Boolean addSpace; /* TRUE if need to add a space before placing
610: * the suffix in the buffer */
611: Buffer buf; /* Buffer in which to store it */
612: {
613: register char *dot;
614:
615: dot = rindex (word, '.');
616: if (dot != (char *)NULL) {
617: if (addSpace) {
618: Buf_AddByte (buf, (Byte)' ');
619: }
620: *dot++ = '\0';
621: Buf_AddBytes (buf, strlen (dot), (Byte *)dot);
622: dot[-1] = '.';
623: return (TRUE);
624: } else {
625: return (addSpace);
626: }
627: }
628:
629: /*-
630: *-----------------------------------------------------------------------
631: * VarRoot --
632: * Remove the suffix of the given word and place the result in the
633: * buffer.
634: *
635: * Results:
636: * TRUE if characters were added to the buffer (a space needs to be
637: * added to the buffer before the next word).
638: *
639: * Side Effects:
640: * The trimmed word is added to the buffer.
641: *
642: *-----------------------------------------------------------------------
643: */
644: static Boolean
645: VarRoot (word, addSpace, buf)
646: char *word; /* Word to trim */
647: Boolean addSpace; /* TRUE if need to add a space to the buffer
648: * before placing the root in it */
649: Buffer buf; /* Buffer in which to store it */
650: {
651: register char *dot;
652:
653: if (addSpace) {
654: Buf_AddByte (buf, (Byte)' ');
655: }
656:
657: dot = rindex (word, '.');
658: if (dot != (char *)NULL) {
659: *dot = '\0';
660: Buf_AddBytes (buf, strlen (word), (Byte *)word);
661: *dot = '.';
662: } else {
663: Buf_AddBytes (buf, strlen(word), (Byte *)word);
664: }
665: return (TRUE);
666: }
667:
668: /*-
669: *-----------------------------------------------------------------------
670: * VarMatch --
671: * Place the word in the buffer if it matches the given pattern.
672: * Callback function for VarModify to implement the :M modifier.
673: *
674: * Results:
675: * TRUE if a space should be placed in the buffer before the next
676: * word.
677: *
678: * Side Effects:
679: * The word may be copied to the buffer.
680: *
681: *-----------------------------------------------------------------------
682: */
683: static Boolean
684: VarMatch (word, addSpace, buf, pattern)
685: char *word; /* Word to examine */
686: Boolean addSpace; /* TRUE if need to add a space to the
687: * buffer before adding the word, if it
688: * matches */
689: Buffer buf; /* Buffer in which to store it */
690: char *pattern; /* Pattern the word must match */
691: {
692: if (Str_Match(word, pattern)) {
693: if (addSpace) {
694: Buf_AddByte(buf, (Byte)' ');
695: }
696: addSpace = TRUE;
697: Buf_AddBytes(buf, strlen(word), (Byte *)word);
698: }
699: return(addSpace);
700: }
701:
702: /*-
703: *-----------------------------------------------------------------------
704: * VarNoMatch --
705: * Place the word in the buffer if it doesn't match the given pattern.
706: * Callback function for VarModify to implement the :N modifier.
707: *
708: * Results:
709: * TRUE if a space should be placed in the buffer before the next
710: * word.
711: *
712: * Side Effects:
713: * The word may be copied to the buffer.
714: *
715: *-----------------------------------------------------------------------
716: */
717: static Boolean
718: VarNoMatch (word, addSpace, buf, pattern)
719: char *word; /* Word to examine */
720: Boolean addSpace; /* TRUE if need to add a space to the
721: * buffer before adding the word, if it
722: * matches */
723: Buffer buf; /* Buffer in which to store it */
724: char *pattern; /* Pattern the word must match */
725: {
726: if (!Str_Match(word, pattern)) {
727: if (addSpace) {
728: Buf_AddByte(buf, (Byte)' ');
729: }
730: addSpace = TRUE;
731: Buf_AddBytes(buf, strlen(word), (Byte *)word);
732: }
733: return(addSpace);
734: }
735:
736: typedef struct {
737: char *lhs; /* String to match */
738: int leftLen; /* Length of string */
739: char *rhs; /* Replacement string (w/ &'s removed) */
740: int rightLen; /* Length of replacement */
741: int flags;
742: #define VAR_SUB_GLOBAL 1 /* Apply substitution globally */
743: #define VAR_MATCH_START 2 /* Match at start of word */
744: #define VAR_MATCH_END 4 /* Match at end of word */
745: #define VAR_NO_SUB 8 /* Substitution is non-global and already done */
746: } VarPattern;
747:
748: /*-
749: *-----------------------------------------------------------------------
750: * VarSubstitute --
751: * Perform a string-substitution on the given word, placing the
752: * result in the passed buffer.
753: *
754: * Results:
755: * TRUE if a space is needed before more characters are added.
756: *
757: * Side Effects:
758: * None.
759: *
760: *-----------------------------------------------------------------------
761: */
762: static Boolean
763: VarSubstitute (word, addSpace, buf, pattern)
764: char *word; /* Word to modify */
765: Boolean addSpace; /* True if space should be added before
766: * other characters */
767: Buffer buf; /* Buffer for result */
768: register VarPattern *pattern; /* Pattern for substitution */
769: {
770: register int wordLen; /* Length of word */
771: register char *cp; /* General pointer */
772:
773: wordLen = strlen(word);
774: if ((pattern->flags & VAR_NO_SUB) == 0) {
775: /*
776: * Still substituting -- break it down into simple anchored cases
777: * and if none of them fits, perform the general substitution case.
778: */
779: if ((pattern->flags & VAR_MATCH_START) &&
780: (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
781: /*
782: * Anchored at start and beginning of word matches pattern
783: */
784: if ((pattern->flags & VAR_MATCH_END) &&
785: (wordLen == pattern->leftLen)) {
786: /*
787: * Also anchored at end and matches to the end (word
788: * is same length as pattern) add space and rhs only
789: * if rhs is non-null.
790: */
791: if (pattern->rightLen != 0) {
792: if (addSpace) {
793: Buf_AddByte(buf, (Byte)' ');
794: }
795: addSpace = TRUE;
796: Buf_AddBytes(buf, pattern->rightLen,
797: (Byte *)pattern->rhs);
798: }
799: } else if (pattern->flags & VAR_MATCH_END) {
800: /*
801: * Doesn't match to end -- copy word wholesale
802: */
803: goto nosub;
804: } else {
805: /*
806: * Matches at start but need to copy in trailing characters
807: */
808: if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
809: if (addSpace) {
810: Buf_AddByte(buf, (Byte)' ');
811: }
812: addSpace = TRUE;
813: }
814: Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
815: Buf_AddBytes(buf, wordLen - pattern->leftLen,
816: (Byte *)(word + pattern->leftLen));
817: }
818: } else if (pattern->flags & VAR_MATCH_START) {
819: /*
820: * Had to match at start of word and didn't -- copy whole word.
821: */
822: goto nosub;
823: } else if (pattern->flags & VAR_MATCH_END) {
824: /*
825: * Anchored at end, Find only place match could occur (leftLen
826: * characters from the end of the word) and see if it does. Note
827: * that because the $ will be left at the end of the lhs, we have
828: * to use strncmp.
829: */
830: cp = word + (wordLen - pattern->leftLen);
831: if ((cp >= word) &&
832: (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
833: /*
834: * Match found. If we will place characters in the buffer,
835: * add a space before hand as indicated by addSpace, then
836: * stuff in the initial, unmatched part of the word followed
837: * by the right-hand-side.
838: */
839: if (((cp - word) + pattern->rightLen) != 0) {
840: if (addSpace) {
841: Buf_AddByte(buf, (Byte)' ');
842: }
843: addSpace = TRUE;
844: }
845: Buf_AddBytes(buf, cp - word, (Byte *)word);
846: Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
847: } else {
848: /*
849: * Had to match at end and didn't. Copy entire word.
850: */
851: goto nosub;
852: }
853: } else {
854: /*
855: * Pattern is unanchored: search for the pattern in the word using
856: * String_FindSubstring, copying unmatched portions and the
857: * right-hand-side for each match found, handling non-global
858: * subsititutions correctly, etc. When the loop is done, any
859: * remaining part of the word (word and wordLen are adjusted
860: * accordingly through the loop) is copied straight into the
861: * buffer.
862: * addSpace is set FALSE as soon as a space is added to the
863: * buffer.
864: */
865: register Boolean done;
866: int origSize;
867:
868: done = FALSE;
869: origSize = Buf_Size(buf);
870: while (!done) {
871: cp = Str_FindSubstring(word, pattern->lhs);
872: if (cp != (char *)NULL) {
873: if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
874: Buf_AddByte(buf, (Byte)' ');
875: addSpace = FALSE;
876: }
877: Buf_AddBytes(buf, cp-word, (Byte *)word);
878: Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
879: wordLen -= (cp - word) + pattern->leftLen;
880: word = cp + pattern->leftLen;
881: if (wordLen == 0) {
882: done = TRUE;
883: }
884: if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
885: done = TRUE;
886: pattern->flags |= VAR_NO_SUB;
887: }
888: } else {
889: done = TRUE;
890: }
891: }
892: if (wordLen != 0) {
893: if (addSpace) {
894: Buf_AddByte(buf, (Byte)' ');
895: }
896: Buf_AddBytes(buf, wordLen, (Byte *)word);
897: }
898: /*
899: * If added characters to the buffer, need to add a space
900: * before we add any more. If we didn't add any, just return
901: * the previous value of addSpace.
902: */
903: return ((Buf_Size(buf) != origSize) || addSpace);
904: }
905: /*
906: * Common code for anchored substitutions: if performed a substitution
907: * and it's not supposed to be global, mark the pattern as requiring
908: * no more substitutions. addSpace was set TRUE if characters were
909: * added to the buffer.
910: */
911: if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
912: pattern->flags |= VAR_NO_SUB;
913: }
914: return (addSpace);
915: }
916: nosub:
917: if (addSpace) {
918: Buf_AddByte(buf, (Byte)' ');
919: }
920: Buf_AddBytes(buf, wordLen, (Byte *)word);
921: return(TRUE);
922: }
923:
924: /*-
925: *-----------------------------------------------------------------------
926: * VarModify --
927: * Modify each of the words of the passed string using the given
928: * function. Used to implement all modifiers.
929: *
930: * Results:
931: * A string of all the words modified appropriately.
932: *
933: * Side Effects:
934: * None.
935: *
936: *-----------------------------------------------------------------------
937: */
938: static char *
939: VarModify (str, modProc, datum)
940: char *str; /* String whose words should be trimmed */
941: Boolean (*modProc)(); /* Function to use to modify them */
942: ClientData datum; /* Datum to pass it */
943: {
944: Buffer buf; /* Buffer for the new string */
945: register char *cp; /* Pointer to end of current word */
946: char endc; /* Character that ended the word */
947: Boolean addSpace; /* TRUE if need to add a space to the
948: * buffer before adding the trimmed
949: * word */
950:
951: buf = Buf_Init (0);
952: cp = str;
953: addSpace = FALSE;
954:
955: while (1) {
956: /*
957: * Skip to next word and place cp at its end.
958: */
959: while (isspace (*str)) {
960: str++;
961: }
962: for (cp = str; *cp != '\0' && !isspace (*cp); cp++) {
963: /* void */ ;
964: }
965: if (cp == str) {
966: /*
967: * If we didn't go anywhere, we must be done!
968: */
969: Buf_AddByte (buf, '\0');
970: str = (char *)Buf_GetAll (buf, (int *)NULL);
971: Buf_Destroy (buf, FALSE);
972: return (str);
973: }
974: /*
975: * Nuke terminating character, but save it in endc b/c if str was
976: * some variable's value, it would not be good to screw it
977: * over...
978: */
979: endc = *cp;
980: *cp = '\0';
981:
982: addSpace = (* modProc) (str, addSpace, buf, datum);
983:
984: if (endc) {
985: *cp++ = endc;
986: }
987: str = cp;
988: }
989: }
990:
991: /*-
992: *-----------------------------------------------------------------------
993: * Var_Parse --
994: * Given the start of a variable invocation, extract the variable
995: * name and find its value, then modify it according to the
996: * specification.
997: *
998: * Results:
999: * The (possibly-modified) value of the variable or var_Error if the
1000: * specification is invalid. The length of the specification is
1001: * placed in *lengthPtr (for invalid specifications, this is just
1002: * 2...?).
1003: * A Boolean in *freePtr telling whether the returned string should
1004: * be freed by the caller.
1005: *
1006: * Side Effects:
1007: * None.
1008: *
1009: *-----------------------------------------------------------------------
1010: */
1011: char *
1012: Var_Parse (str, ctxt, err, lengthPtr, freePtr)
1013: char *str; /* The string to parse */
1014: GNode *ctxt; /* The context for the variable */
1015: Boolean err; /* TRUE if undefined variables are an error */
1016: int *lengthPtr; /* OUT: The length of the specification */
1017: Boolean *freePtr; /* OUT: TRUE if caller should free result */
1018: {
1019: register char *tstr; /* Pointer into str */
1020: Var *v; /* Variable in invocation */
1021: register char *cp; /* Secondary pointer into str (place marker
1022: * for tstr) */
1023: Boolean haveModifier;/* TRUE if have modifiers for the variable */
1024: register char endc; /* Ending character when variable in parens
1025: * or braces */
1026: char *start;
1027: Boolean dynamic; /* TRUE if the variable is local and we're
1028: * expanding it in a non-local context. This
1029: * is done to support dynamic sources. The
1030: * result is just the invocation, unaltered */
1031:
1032: *freePtr = FALSE;
1033: dynamic = FALSE;
1034: start = str;
1035:
1036: if (str[1] != '(' && str[1] != '{') {
1037: /*
1038: * If it's not bounded by braces of some sort, life is much simpler.
1039: * We just need to check for the first character and return the
1040: * value if it exists.
1041: */
1042: char name[2];
1043:
1044: name[0] = str[1];
1045: name[1] = '\0';
1046:
1047: v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1048: if (v == (Var *)NIL) {
1049: *lengthPtr = 2;
1050:
1051: if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
1052: /*
1053: * If substituting a local variable in a non-local context,
1054: * assume it's for dynamic source stuff. We have to handle
1055: * this specially and return the longhand for the variable
1056: * with the dollar sign escaped so it makes it back to the
1057: * caller. Only four of the local variables are treated
1058: * specially as they are the only four that will be set
1059: * when dynamic sources are expanded.
1060: */
1061: switch (str[1]) {
1062: case '@':
1063: return("$(.TARGET)");
1064: case '%':
1065: return("$(.ARCHIVE)");
1066: case '*':
1067: return("$(.PREFIX)");
1068: case '!':
1069: return("$(.MEMBER)");
1070: }
1071: }
1072: /*
1073: * Error
1074: */
1075: return (err ? var_Error : varNoError);
1076: } else {
1077: haveModifier = FALSE;
1078: tstr = &str[1];
1079: endc = str[1];
1080: }
1081: } else {
1082: endc = str[1] == '(' ? ')' : '}';
1083:
1084: /*
1085: * Skip to the end character or a colon, whichever comes first.
1086: */
1087: for (tstr = str + 2;
1088: *tstr != '\0' && *tstr != endc && *tstr != ':';
1089: tstr++)
1090: {
1091: continue;
1092: }
1093: if (*tstr == ':') {
1094: haveModifier = TRUE;
1095: } else if (*tstr != '\0') {
1096: haveModifier = FALSE;
1097: } else {
1098: /*
1099: * If we never did find the end character, return NULL
1100: * right now, setting the length to be the distance to
1101: * the end of the string, since that's what make does.
1102: */
1103: *lengthPtr = tstr - str;
1104: return (var_Error);
1105: }
1106: *tstr = '\0';
1107:
1108: v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1109: if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
1110: ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D'))
1111: {
1112: /*
1113: * Check for bogus D and F forms of local variables since we're
1114: * in a local context and the name is the right length.
1115: */
1116: switch(str[2]) {
1117: case '@':
1118: case '%':
1119: case '*':
1120: case '!':
1121: case '>':
1122: case '<':
1123: {
1124: char vname[2];
1125: char *val;
1126:
1127: /*
1128: * Well, it's local -- go look for it.
1129: */
1130: vname[0] = str[2];
1131: vname[1] = '\0';
1132: v = VarFind(vname, ctxt, 0);
1133:
1134: if (v != (Var *)NIL) {
1135: /*
1136: * No need for nested expansion or anything, as we're
1137: * the only one who sets these things and we sure don't
1138: * but nested invocations in them...
1139: */
1140: val = (char *)Buf_GetAll(v->val, (int *)NULL);
1141:
1142: if (str[3] == 'D') {
1143: val = VarModify(val, VarHead, (ClientData)0);
1144: } else {
1145: val = VarModify(val, VarTail, (ClientData)0);
1146: }
1147: /*
1148: * Resulting string is dynamically allocated, so
1149: * tell caller to free it.
1150: */
1151: *freePtr = TRUE;
1152: *lengthPtr = tstr-start+1;
1153: *tstr = endc;
1154: return(val);
1155: }
1156: break;
1157: }
1158: }
1159: }
1160:
1161: if (v == (Var *)NIL) {
1162: if ((((tstr-str) == 3) ||
1163: ((((tstr-str) == 4) && (str[3] == 'F' ||
1164: str[3] == 'D')))) &&
1165: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1166: {
1167: /*
1168: * If substituting a local variable in a non-local context,
1169: * assume it's for dynamic source stuff. We have to handle
1170: * this specially and return the longhand for the variable
1171: * with the dollar sign escaped so it makes it back to the
1172: * caller. Only four of the local variables are treated
1173: * specially as they are the only four that will be set
1174: * when dynamic sources are expanded.
1175: */
1176: switch (str[2]) {
1177: case '@':
1178: case '%':
1179: case '*':
1180: case '!':
1181: dynamic = TRUE;
1182: break;
1183: }
1184: } else if (((tstr-str) > 4) && (str[2] == '.') &&
1185: isupper(str[3]) &&
1186: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1187: {
1188: int len;
1189:
1190: len = (tstr-str) - 3;
1191: if ((strncmp(str+2, ".TARGET", len) == 0) ||
1192: (strncmp(str+2, ".ARCHIVE", len) == 0) ||
1193: (strncmp(str+2, ".PREFIX", len) == 0) ||
1194: (strncmp(str+2, ".MEMBER", len) == 0))
1195: {
1196: dynamic = TRUE;
1197: }
1198: }
1199:
1200: if (!haveModifier) {
1201: /*
1202: * No modifiers -- have specification length so we can return
1203: * now.
1204: */
1205: *lengthPtr = tstr - start + 1;
1206: *tstr = endc;
1207: if (dynamic) {
1208: str = emalloc(*lengthPtr + 1);
1209: strncpy(str, start, *lengthPtr);
1210: str[*lengthPtr] = '\0';
1211: *freePtr = TRUE;
1212: return(str);
1213: } else {
1214: return (err ? var_Error : varNoError);
1215: }
1216: } else {
1217: /*
1218: * Still need to get to the end of the variable specification,
1219: * so kludge up a Var structure for the modifications
1220: */
1221: v = (Var *) emalloc(sizeof(Var));
1222: v->name = &str[1];
1223: v->val = Buf_Init(1);
1224: v->flags = VAR_JUNK;
1225: }
1226: }
1227: }
1228:
1229: if (v->flags & VAR_IN_USE) {
1230: Fatal("Variable %s is recursive.", v->name);
1231: /*NOTREACHED*/
1232: } else {
1233: v->flags |= VAR_IN_USE;
1234: }
1235: /*
1236: * Before doing any modification, we have to make sure the value
1237: * has been fully expanded. If it looks like recursion might be
1238: * necessary (there's a dollar sign somewhere in the variable's value)
1239: * we just call Var_Subst to do any other substitutions that are
1240: * necessary. Note that the value returned by Var_Subst will have
1241: * been dynamically-allocated, so it will need freeing when we
1242: * return.
1243: */
1244: str = (char *)Buf_GetAll(v->val, (int *)NULL);
1245: if (index (str, '$') != (char *)NULL) {
1246: str = Var_Subst(str, ctxt, err);
1247: *freePtr = TRUE;
1248: }
1249:
1250: v->flags &= ~VAR_IN_USE;
1251:
1252: /*
1253: * Now we need to apply any modifiers the user wants applied.
1254: * These are:
1255: * :M<pattern> words which match the given <pattern>.
1256: * <pattern> is of the standard file
1257: * wildcarding form.
1258: * :S<d><pat1><d><pat2><d>[g]
1259: * Substitute <pat2> for <pat1> in the value
1260: * :H Substitute the head of each word
1261: * :T Substitute the tail of each word
1262: * :E Substitute the extension (minus '.') of
1263: * each word
1264: * :R Substitute the root of each word
1265: * (pathname minus the suffix).
1266: * :lhs=rhs Like :S, but the rhs goes to the end of
1267: * the invocation.
1268: */
1269: if ((str != (char *)NULL) && haveModifier) {
1270: /*
1271: * Skip initial colon while putting it back.
1272: */
1273: *tstr++ = ':';
1274: while (*tstr != endc) {
1275: char *newStr; /* New value to return */
1276: char termc; /* Character which terminated scan */
1277:
1278: if (DEBUG(VAR)) {
1279: printf("Applying :%c to \"%s\"\n", *tstr, str);
1280: }
1281: switch (*tstr) {
1282: case 'N':
1283: case 'M':
1284: {
1285: char *pattern;
1286: char *cp2;
1287: Boolean copy;
1288:
1289: copy = FALSE;
1290: for (cp = tstr + 1;
1291: *cp != '\0' && *cp != ':' && *cp != endc;
1292: cp++)
1293: {
1294: if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
1295: copy = TRUE;
1296: cp++;
1297: }
1298: }
1299: termc = *cp;
1300: *cp = '\0';
1301: if (copy) {
1302: /*
1303: * Need to compress the \:'s out of the pattern, so
1304: * allocate enough room to hold the uncompressed
1305: * pattern (note that cp started at tstr+1, so
1306: * cp - tstr takes the null byte into account) and
1307: * compress the pattern into the space.
1308: */
1309: pattern = emalloc(cp - tstr);
1310: for (cp2 = pattern, cp = tstr + 1;
1311: *cp != '\0';
1312: cp++, cp2++)
1313: {
1314: if ((*cp == '\\') &&
1315: (cp[1] == ':' || cp[1] == endc)) {
1316: cp++;
1317: }
1318: *cp2 = *cp;
1319: }
1320: *cp2 = '\0';
1321: } else {
1322: pattern = &tstr[1];
1323: }
1324: if (*tstr == 'M' || *tstr == 'm') {
1325: newStr = VarModify(str, VarMatch, (ClientData)pattern);
1326: } else {
1327: newStr = VarModify(str, VarNoMatch,
1328: (ClientData)pattern);
1329: }
1330: if (copy) {
1331: free(pattern);
1332: }
1333: break;
1334: }
1335: case 'S':
1336: {
1337: VarPattern pattern;
1338: register char delim;
1339: Buffer buf; /* Buffer for patterns */
1340: register char *cp2;
1341: int lefts;
1342:
1343: pattern.flags = 0;
1344: delim = tstr[1];
1345: tstr += 2;
1346: /*
1347: * If pattern begins with '^', it is anchored to the
1348: * start of the word -- skip over it and flag pattern.
1349: */
1350: if (*tstr == '^') {
1351: pattern.flags |= VAR_MATCH_START;
1352: tstr += 1;
1353: }
1354:
1355: buf = Buf_Init(0);
1356:
1357: /*
1358: * Pass through the lhs looking for 1) escaped delimiters,
1359: * '$'s and backslashes (place the escaped character in
1360: * uninterpreted) and 2) unescaped $'s that aren't before
1361: * the delimiter (expand the variable substitution).
1362: * The result is left in the Buffer buf.
1363: */
1364: for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
1365: if ((*cp == '\\') &&
1366: ((cp[1] == delim) ||
1367: (cp[1] == '$') ||
1368: (cp[1] == '\\')))
1369: {
1370: Buf_AddByte(buf, (Byte)cp[1]);
1371: cp++;
1372: } else if (*cp == '$') {
1373: if (cp[1] != delim) {
1374: /*
1375: * If unescaped dollar sign not before the
1376: * delimiter, assume it's a variable
1377: * substitution and recurse.
1378: */
1379: char *cp2;
1380: int len;
1381: Boolean freeIt;
1382:
1383: cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
1384: Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
1385: if (freeIt) {
1386: free(cp2);
1387: }
1388: cp += len - 1;
1389: } else {
1390: /*
1391: * Unescaped $ at end of pattern => anchor
1392: * pattern at end.
1393: */
1394: pattern.flags |= VAR_MATCH_END;
1395: }
1396: } else {
1397: Buf_AddByte(buf, (Byte)*cp);
1398: }
1399: }
1400:
1401: Buf_AddByte(buf, (Byte)'\0');
1402:
1403: /*
1404: * If lhs didn't end with the delimiter, complain and
1405: * return NULL
1406: */
1407: if (*cp != delim) {
1408: *lengthPtr = cp - start + 1;
1409: if (*freePtr) {
1410: free(str);
1411: }
1412: Buf_Destroy(buf, TRUE);
1413: Error("Unclosed substitution for %s (%c missing)",
1414: v->name, delim);
1415: return (var_Error);
1416: }
1417:
1418: /*
1419: * Fetch pattern and destroy buffer, but preserve the data
1420: * in it, since that's our lhs. Note that Buf_GetAll
1421: * will return the actual number of bytes, which includes
1422: * the null byte, so we have to decrement the length by
1423: * one.
1424: */
1425: pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen);
1426: pattern.leftLen--;
1427: Buf_Destroy(buf, FALSE);
1428:
1429: /*
1430: * Now comes the replacement string. Three things need to
1431: * be done here: 1) need to compress escaped delimiters and
1432: * ampersands and 2) need to replace unescaped ampersands
1433: * with the l.h.s. (since this isn't regexp, we can do
1434: * it right here) and 3) expand any variable substitutions.
1435: */
1436: buf = Buf_Init(0);
1437:
1438: tstr = cp + 1;
1439: for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
1440: if ((*cp == '\\') &&
1441: ((cp[1] == delim) ||
1442: (cp[1] == '&') ||
1443: (cp[1] == '\\') ||
1444: (cp[1] == '$')))
1445: {
1446: Buf_AddByte(buf, (Byte)cp[1]);
1447: cp++;
1448: } else if ((*cp == '$') && (cp[1] != delim)) {
1449: char *cp2;
1450: int len;
1451: Boolean freeIt;
1452:
1453: cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
1454: Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
1455: cp += len - 1;
1456: if (freeIt) {
1457: free(cp2);
1458: }
1459: } else if (*cp == '&') {
1460: Buf_AddBytes(buf, pattern.leftLen,
1461: (Byte *)pattern.lhs);
1462: } else {
1463: Buf_AddByte(buf, (Byte)*cp);
1464: }
1465: }
1466:
1467: Buf_AddByte(buf, (Byte)'\0');
1468:
1469: /*
1470: * If didn't end in delimiter character, complain
1471: */
1472: if (*cp != delim) {
1473: *lengthPtr = cp - start + 1;
1474: if (*freePtr) {
1475: free(str);
1476: }
1477: Buf_Destroy(buf, TRUE);
1478: Error("Unclosed substitution for %s (%c missing)",
1479: v->name, delim);
1480: return (var_Error);
1481: }
1482:
1483: pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen);
1484: pattern.rightLen--;
1485: Buf_Destroy(buf, FALSE);
1486:
1487: /*
1488: * Check for global substitution. If 'g' after the final
1489: * delimiter, substitution is global and is marked that
1490: * way.
1491: */
1492: cp++;
1493: if (*cp == 'g') {
1494: pattern.flags |= VAR_SUB_GLOBAL;
1495: cp++;
1496: }
1497:
1498: termc = *cp;
1499: newStr = VarModify(str, VarSubstitute,
1500: (ClientData)&pattern);
1501: /*
1502: * Free the two strings.
1503: */
1504: free(pattern.lhs);
1505: free(pattern.rhs);
1506: break;
1507: }
1508: case 'T':
1509: if (tstr[1] == endc || tstr[1] == ':') {
1510: newStr = VarModify (str, VarTail, (ClientData)0);
1511: cp = tstr + 1;
1512: termc = *cp;
1513: break;
1514: }
1515: /*FALLTHRU*/
1516: case 'H':
1517: if (tstr[1] == endc || tstr[1] == ':') {
1518: newStr = VarModify (str, VarHead, (ClientData)0);
1519: cp = tstr + 1;
1520: termc = *cp;
1521: break;
1522: }
1523: /*FALLTHRU*/
1524: case 'E':
1525: if (tstr[1] == endc || tstr[1] == ':') {
1526: newStr = VarModify (str, VarSuffix, (ClientData)0);
1527: cp = tstr + 1;
1528: termc = *cp;
1529: break;
1530: }
1531: /*FALLTHRU*/
1532: case 'R':
1533: if (tstr[1] == endc || tstr[1] == ':') {
1534: newStr = VarModify (str, VarRoot, (ClientData)0);
1535: cp = tstr + 1;
1536: termc = *cp;
1537: break;
1538: }
1539: /*FALLTHRU*/
1540: default: {
1541: /*
1542: * This can either be a bogus modifier or a System-V
1543: * substitution command.
1544: */
1545: VarPattern pattern;
1546: Boolean eqFound;
1547:
1548: pattern.flags = 0;
1549: eqFound = FALSE;
1550: /*
1551: * First we make a pass through the string trying
1552: * to verify it is a SYSV-make-style translation:
1553: * it must be: <string1>=<string2>)
1554: */
1555: for (cp = tstr; *cp != '\0' && *cp != endc; cp++) {
1556: if (*cp == '=') {
1557: eqFound = TRUE;
1558: /* continue looking for endc */
1559: }
1560: }
1561: if (*cp == endc && eqFound) {
1562:
1563: /*
1564: * Now we break this sucker into the lhs and
1565: * rhs. We must null terminate them of course.
1566: */
1567: for (cp = tstr; *cp != '='; cp++) {
1568: ;
1569: }
1570: pattern.lhs = tstr;
1571: pattern.leftLen = cp - tstr;
1572: *cp++ = '\0';
1573:
1574: pattern.rhs = cp;
1575: while (*cp != endc) {
1576: cp++;
1577: }
1578: pattern.rightLen = cp - pattern.rhs;
1579: *cp = '\0';
1580:
1581: /*
1582: * SYSV modifications happen through the whole
1583: * string. Note the pattern is anchored at the end.
1584: */
1585: pattern.flags |= VAR_SUB_GLOBAL|VAR_MATCH_END;
1586:
1587: newStr = VarModify(str, VarSubstitute,
1588: (ClientData)&pattern);
1589:
1590: /*
1591: * Restore the nulled characters
1592: */
1593: pattern.lhs[pattern.leftLen] = '=';
1594: pattern.rhs[pattern.rightLen] = endc;
1595: termc = endc;
1596: } else {
1597: Error ("Unknown modifier '%c'\n", *tstr);
1598: for (cp = tstr+1;
1599: *cp != ':' && *cp != endc && *cp != '\0';
1600: cp++) {
1601: ;
1602: }
1603: termc = *cp;
1604: newStr = var_Error;
1605: }
1606: }
1607: }
1608: if (DEBUG(VAR)) {
1609: printf("Result is \"%s\"\n", newStr);
1610: }
1611:
1612: if (*freePtr) {
1613: free (str);
1614: }
1615: str = newStr;
1616: if (str != var_Error) {
1617: *freePtr = TRUE;
1618: } else {
1619: *freePtr = FALSE;
1620: }
1621: if (termc == '\0') {
1622: Error("Unclosed variable specification for %s", v->name);
1623: } else if (termc == ':') {
1624: *cp++ = termc;
1625: } else {
1626: *cp = termc;
1627: }
1628: tstr = cp;
1629: }
1630: *lengthPtr = tstr - start + 1;
1631: } else {
1632: *lengthPtr = tstr - start + 1;
1633: *tstr = endc;
1634: }
1635:
1636: if (v->flags & VAR_FROM_ENV) {
1637: Boolean destroy = FALSE;
1638:
1639: if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) {
1640: destroy = TRUE;
1641: } else {
1642: /*
1643: * Returning the value unmodified, so tell the caller to free
1644: * the thing.
1645: */
1646: *freePtr = TRUE;
1647: }
1648: Buf_Destroy(v->val, destroy);
1649: free((Address)v);
1650: } else if (v->flags & VAR_JUNK) {
1651: /*
1652: * Perform any free'ing needed and set *freePtr to FALSE so the caller
1653: * doesn't try to free a static pointer.
1654: */
1655: if (*freePtr) {
1656: free(str);
1657: }
1658: *freePtr = FALSE;
1659: free((Address)v);
1660: if (dynamic) {
1661: str = emalloc(*lengthPtr + 1);
1662: strncpy(str, start, *lengthPtr);
1663: str[*lengthPtr] = '\0';
1664: *freePtr = TRUE;
1665: } else {
1666: str = var_Error;
1667: }
1668: }
1669: return (str);
1670: }
1671:
1672: /*-
1673: *-----------------------------------------------------------------------
1674: * Var_Subst --
1675: * Substitute for all variables in the given string in the given context
1676: * If undefErr is TRUE, Parse_Error will be called when an undefined
1677: * variable is encountered.
1678: *
1679: * Results:
1680: * The resulting string.
1681: *
1682: * Side Effects:
1683: * None. The old string must be freed by the caller
1684: *-----------------------------------------------------------------------
1685: */
1686: char *
1687: Var_Subst (str, ctxt, undefErr)
1688: register char *str; /* the string in which to substitute */
1689: GNode *ctxt; /* the context wherein to find variables */
1690: Boolean undefErr; /* TRUE if undefineds are an error */
1691: {
1692: Buffer buf; /* Buffer for forming things */
1693: char *val; /* Value to substitute for a variable */
1694: int length; /* Length of the variable invocation */
1695: Boolean doFree; /* Set true if val should be freed */
1696: static Boolean errorReported; /* Set true if an error has already
1697: * been reported to prevent a plethora
1698: * of messages when recursing */
1699:
1700: buf = Buf_Init (BSIZE);
1701: errorReported = FALSE;
1702:
1703: while (*str) {
1704: if ((*str == '$') && (str[1] == '$')) {
1705: /*
1706: * A dollar sign may be escaped either with another dollar sign.
1707: * In such a case, we skip over the escape character and store the
1708: * dollar sign into the buffer directly.
1709: */
1710: str++;
1711: Buf_AddByte(buf, (Byte)*str);
1712: str++;
1713: } else if (*str != '$') {
1714: /*
1715: * Skip as many characters as possible -- either to the end of
1716: * the string or to the next dollar sign (variable invocation).
1717: */
1718: char *cp;
1719:
1720: for (cp = str++; *str != '$' && *str != '\0'; str++) {
1721: ;
1722: }
1723: Buf_AddBytes(buf, str - cp, (Byte *)cp);
1724: } else {
1725: val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
1726:
1727: /*
1728: * When we come down here, val should either point to the
1729: * value of this variable, suitably modified, or be NULL.
1730: * Length should be the total length of the potential
1731: * variable invocation (from $ to end character...)
1732: */
1733: if (val == var_Error || val == varNoError) {
1734: /*
1735: * If performing old-time variable substitution, skip over
1736: * the variable and continue with the substitution. Otherwise,
1737: * store the dollar sign and advance str so we continue with
1738: * the string...
1739: */
1740: if (oldVars) {
1741: str += length;
1742: } else if (undefErr) {
1743: /*
1744: * If variable is undefined, complain and skip the
1745: * variable. The complaint will stop us from doing anything
1746: * when the file is parsed.
1747: */
1748: if (!errorReported) {
1749: Parse_Error (PARSE_FATAL,
1750: "Undefined variable \"%.*s\"",length,str);
1751: }
1752: str += length;
1753: errorReported = TRUE;
1754: } else {
1755: Buf_AddByte (buf, (Byte)*str);
1756: str += 1;
1757: }
1758: } else {
1759: /*
1760: * We've now got a variable structure to store in. But first,
1761: * advance the string pointer.
1762: */
1763: str += length;
1764:
1765: /*
1766: * Copy all the characters from the variable value straight
1767: * into the new string.
1768: */
1769: Buf_AddBytes (buf, strlen (val), (Byte *)val);
1770: if (doFree) {
1771: free ((Address)val);
1772: }
1773: }
1774: }
1775: }
1776:
1777: Buf_AddByte (buf, '\0');
1778: str = (char *)Buf_GetAll (buf, (int *)NULL);
1779: Buf_Destroy (buf, FALSE);
1780: return (str);
1781: }
1782:
1783: /*-
1784: *-----------------------------------------------------------------------
1785: * Var_GetTail --
1786: * Return the tail from each of a list of words. Used to set the
1787: * System V local variables.
1788: *
1789: * Results:
1790: * The resulting string.
1791: *
1792: * Side Effects:
1793: * None.
1794: *
1795: *-----------------------------------------------------------------------
1796: */
1797: char *
1798: Var_GetTail(file)
1799: char *file; /* Filename to modify */
1800: {
1801: return(VarModify(file, VarTail, (ClientData)0));
1802: }
1803:
1804: /*-
1805: *-----------------------------------------------------------------------
1806: * Var_GetHead --
1807: * Find the leading components of a (list of) filename(s).
1808: * XXX: VarHead does not replace foo by ., as (sun) System V make
1809: * does.
1810: *
1811: * Results:
1812: * The leading components.
1813: *
1814: * Side Effects:
1815: * None.
1816: *
1817: *-----------------------------------------------------------------------
1818: */
1819: char *
1820: Var_GetHead(file)
1821: char *file; /* Filename to manipulate */
1822: {
1823: return(VarModify(file, VarHead, (ClientData)0));
1824: }
1825:
1826: /*-
1827: *-----------------------------------------------------------------------
1828: * Var_Init --
1829: * Initialize the module
1830: *
1831: * Results:
1832: * None
1833: *
1834: * Side Effects:
1835: * The VAR_CMD and VAR_GLOBAL contexts are created
1836: *-----------------------------------------------------------------------
1837: */
1838: void
1839: Var_Init ()
1840: {
1841: VAR_GLOBAL = Targ_NewGN ("Global");
1842: VAR_CMD = Targ_NewGN ("Command");
1843:
1844: }
1845:
1846: /****************** PRINT DEBUGGING INFO *****************/
1847: static
1848: VarPrintVar (v)
1849: Var *v;
1850: {
1851: printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, (int *)NULL));
1852: return (0);
1853: }
1854:
1855: /*-
1856: *-----------------------------------------------------------------------
1857: * Var_Dump --
1858: * print all variables in a context
1859: *-----------------------------------------------------------------------
1860: */
1861: Var_Dump (ctxt)
1862: GNode *ctxt;
1863: {
1864: Lst_ForEach (ctxt->context, VarPrintVar);
1865: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.