1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 
  27 /*
  28  *      macro.cc
  29  *
  30  *      Handle expansion of make macros
  31  */
  32 
  33 /*
  34  * Included files
  35  */
  36 #include <mksh/dosys.h>           /* sh_command2string() */
  37 #include <mksh/i18n.h>            /* get_char_semantics_value() */
  38 #include <mksh/macro.h>
  39 #include <mksh/misc.h>            /* retmem() */
  40 #include <mksh/read.h>            /* get_next_block_fn() */
  41 
  42 #include <libintl.h>
  43 
  44 /*
  45  * File table of contents
  46  */
  47 static void     add_macro_to_global_list(Name macro_to_add);
  48 static void     expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd);
  49 
  50 static void     init_arch_macros(void);
  51 static void     init_mach_macros(void);
  52 static Boolean  init_arch_done = false;
  53 static Boolean  init_mach_done = false;
  54 
  55 
  56 long env_alloc_num = 0;
  57 long env_alloc_bytes = 0;
  58 
  59 /*
  60  *      getvar(name)
  61  *
  62  *      Return expanded value of macro.
  63  *
  64  *      Return value:
  65  *                              The expanded value of the macro
  66  *
  67  *      Parameters:
  68  *              name            The name of the macro we want the value for
  69  *
  70  *      Global variables used:
  71  */
  72 Name
  73 getvar(register Name name)
  74 {
  75         String_rec              destination;
  76         wchar_t                 buffer[STRING_BUFFER_LENGTH];
  77         register Name           result;
  78 
  79         if ((name == host_arch) || (name == target_arch)) {
  80                 if (!init_arch_done) {
  81                         init_arch_done = true;
  82                         init_arch_macros();
  83                 }
  84         }
  85         if ((name == host_mach) || (name == target_mach)) {
  86                 if (!init_mach_done) {
  87                         init_mach_done = true;
  88                         init_mach_macros();
  89                 }
  90         }
  91 
  92         INIT_STRING_FROM_STACK(destination, buffer);
  93         expand_value(maybe_append_prop(name, macro_prop)->body.macro.value,
  94                      &destination,
  95                      false);
  96         result = GETNAME(destination.buffer.start, FIND_LENGTH);
  97         if (destination.free_after_use) {
  98                 retmem(destination.buffer.start);
  99         }
 100         return result;
 101 }
 102 
 103 /*
 104  *      expand_value(value, destination, cmd)
 105  *
 106  *      Recursively expands all macros in the string value.
 107  *      destination is where the expanded value should be appended.
 108  *
 109  *      Parameters:
 110  *              value           The value we are expanding
 111  *              destination     Where to deposit the expansion
 112  *              cmd             If we are evaluating a command line we
 113  *                              turn \ quoting off
 114  *
 115  *      Global variables used:
 116  */
 117 void
 118 expand_value(Name value, register String destination, Boolean cmd)
 119 {
 120         Source_rec              sourceb;
 121         register Source         source = &sourceb;
 122         register wchar_t        *source_p = NULL;
 123         register wchar_t        *source_end = NULL;
 124         wchar_t                 *block_start = NULL;
 125         int                     quote_seen = 0;
 126 
 127         if (value == NULL) {
 128                 /*
 129                  * Make sure to get a string allocated even if it
 130                  * will be empty.
 131                  */
 132                 MBSTOWCS(wcs_buffer, "");
 133                 append_string(wcs_buffer, destination, FIND_LENGTH);
 134                 destination->text.end = destination->text.p;
 135                 return;
 136         }
 137         if (!value->dollar) {
 138                 /*
 139                  * If the value we are expanding does not contain
 140                  * any $, we don't have to parse it.
 141                  */
 142                 APPEND_NAME(value,
 143                         destination,
 144                         (int) value->hash.length
 145                 );
 146                 destination->text.end = destination->text.p;
 147                 return;
 148         }
 149 
 150         if (value->being_expanded) {
 151                 fatal_reader_mksh(gettext("Loop detected when expanding macro value `%s'"),
 152                              value->string_mb);
 153         }
 154         value->being_expanded = true;
 155         /* Setup the structure we read from */
 156         Wstring vals(value);
 157         sourceb.string.text.p = sourceb.string.buffer.start = wcsdup(vals.get_string());
 158         sourceb.string.free_after_use = true;
 159         sourceb.string.text.end =
 160           sourceb.string.buffer.end =
 161             sourceb.string.text.p + value->hash.length;
 162         sourceb.previous = NULL;
 163         sourceb.fd = -1;
 164         sourceb.inp_buf =
 165           sourceb.inp_buf_ptr =
 166             sourceb.inp_buf_end = NULL;
 167         sourceb.error_converting = false;
 168         /* Lift some pointers from the struct to local register variables */
 169         CACHE_SOURCE(0);
 170 /* We parse the string in segments */
 171 /* We read chars until we find a $, then we append what we have read so far */
 172 /* (since last $ processing) to the destination. When we find a $ we call */
 173 /* expand_macro() and let it expand that particular $ reference into dest */
 174         block_start = source_p;
 175         quote_seen = 0;
 176         for (; 1; source_p++) {
 177                 switch (GET_CHAR()) {
 178                 case backslash_char:
 179                         /* Quote $ in macro value */
 180                         if (!cmd) {
 181                                 quote_seen = ~quote_seen;
 182                         }
 183                         continue;
 184                 case dollar_char:
 185                         /* Save the plain string we found since */
 186                         /* start of string or previous $ */
 187                         if (quote_seen) {
 188                                 append_string(block_start,
 189                                               destination,
 190                                               source_p - block_start - 1);
 191                                 block_start = source_p;
 192                                 break;
 193                         }
 194                         append_string(block_start,
 195                                       destination,
 196                                       source_p - block_start);
 197                         source->string.text.p = ++source_p;
 198                         UNCACHE_SOURCE();
 199                         /* Go expand the macro reference */
 200                         expand_macro(source, destination, sourceb.string.buffer.start, cmd);
 201                         CACHE_SOURCE(1);
 202                         block_start = source_p + 1;
 203                         break;
 204                 case nul_char:
 205                         /* The string ran out. Get some more */
 206                         append_string(block_start,
 207                                       destination,
 208                                       source_p - block_start);
 209                         GET_NEXT_BLOCK_NOCHK(source);
 210                         if (source == NULL) {
 211                                 destination->text.end = destination->text.p;
 212                                 value->being_expanded = false;
 213                                 return;
 214                         }
 215                         if (source->error_converting) {
 216                                 fatal_reader_mksh("Internal error: Invalid byte sequence in expand_value()");
 217                         }
 218                         block_start = source_p;
 219                         source_p--;
 220                         continue;
 221                 }
 222                 quote_seen = 0;
 223         }
 224         retmem(sourceb.string.buffer.start);
 225 }
 226 
 227 /*
 228  *      expand_macro(source, destination, current_string, cmd)
 229  *
 230  *      Should be called with source->string.text.p pointing to
 231  *      the first char after the $ that starts a macro reference.
 232  *      source->string.text.p is returned pointing to the first char after
 233  *      the macro name.
 234  *      It will read the macro name, expanding any macros in it,
 235  *      and get the value. The value is then expanded.
 236  *      destination is a String that is filled in with the expanded macro.
 237  *      It may be passed in referencing a buffer to expand the macro into.
 238  *      Note that most expansions are done on demand, e.g. right 
 239  *      before the command is executed and not while the file is
 240  *      being parsed.
 241  *
 242  *      Parameters:
 243  *              source          The source block that references the string
 244  *                              to expand
 245  *              destination     Where to put the result
 246  *              current_string  The string we are expanding, for error msg
 247  *              cmd             If we are evaluating a command line we
 248  *                              turn \ quoting off
 249  *
 250  *      Global variables used:
 251  *              funny           Vector of semantic tags for characters
 252  *              is_conditional  Set if a conditional macro is refd
 253  *              make_word_mentioned Set if the word "MAKE" is mentioned
 254  *              makefile_type   We deliver extra msg when reading makefiles
 255  *              query           The Name "?", compared against
 256  *              query_mentioned Set if the word "?" is mentioned
 257  */
 258 void
 259 expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd)
 260 {
 261         static Name             make = (Name)NULL;
 262         static wchar_t          colon_sh[4];
 263         static wchar_t          colon_shell[7];
 264         String_rec              string;
 265         wchar_t                 buffer[STRING_BUFFER_LENGTH];
 266         register wchar_t        *source_p = source->string.text.p;
 267         register wchar_t        *source_end = source->string.text.end;
 268         register int            closer = 0;
 269         wchar_t                 *block_start = (wchar_t *)NULL;
 270         int                     quote_seen = 0;
 271         register int            closer_level = 1;
 272         Name                    name = (Name)NULL;
 273         wchar_t                 *colon = (wchar_t *)NULL;
 274         wchar_t                 *percent = (wchar_t *)NULL;
 275         wchar_t                 *eq = (wchar_t *) NULL;
 276         Property                macro = NULL;
 277         wchar_t                 *p = (wchar_t*)NULL;
 278         String_rec              extracted;
 279         wchar_t                 extracted_string[MAXPATHLEN];
 280         wchar_t                 *left_head = NULL;
 281         wchar_t                 *left_tail = NULL;
 282         wchar_t                 *right_tail = NULL;
 283         int                     left_head_len = 0;
 284         int                     left_tail_len = 0;
 285         int                     tmp_len = 0;
 286         wchar_t                 *right_hand[128];
 287         int                     i = 0;
 288         enum {
 289                 no_extract,
 290                 dir_extract,
 291                 file_extract
 292         }                       extraction = no_extract;
 293         enum {
 294                 no_replace,
 295                 suffix_replace,
 296                 pattern_replace,
 297                 sh_replace
 298         }                       replacement = no_replace;
 299 
 300         if (make == NULL) {
 301                 MBSTOWCS(wcs_buffer, "MAKE");
 302                 make = GETNAME(wcs_buffer, FIND_LENGTH);
 303 
 304                 MBSTOWCS(colon_sh, ":sh");
 305                 MBSTOWCS(colon_shell, ":shell");
 306         }
 307 
 308         right_hand[0] = NULL;
 309 
 310         /* First copy the (macro-expanded) macro name into string. */
 311         INIT_STRING_FROM_STACK(string, buffer);
 312 recheck_first_char:
 313         /* Check the first char of the macro name to figure out what to do. */
 314         switch (GET_CHAR()) {
 315         case nul_char:
 316                 GET_NEXT_BLOCK_NOCHK(source);
 317                 if (source == NULL) {
 318                         WCSTOMBS(mbs_buffer, current_string);
 319                         fatal_reader_mksh(gettext("'$' at end of string `%s'"),
 320                                      mbs_buffer);
 321                 }
 322                 if (source->error_converting) {
 323                         fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()");
 324                 }
 325                 goto recheck_first_char;
 326         case parenleft_char:
 327                 /* Multi char name. */
 328                 closer = (int) parenright_char;
 329                 break;
 330         case braceleft_char:
 331                 /* Multi char name. */
 332                 closer = (int) braceright_char;
 333                 break;
 334         case newline_char:
 335                 fatal_reader_mksh(gettext("'$' at end of line"));
 336         default:
 337                 /* Single char macro name. Just suck it up */
 338                 append_char(*source_p, &string);
 339                 source->string.text.p = source_p + 1;
 340                 goto get_macro_value;
 341         }
 342 
 343         /* Handle multi-char macro names */
 344         block_start = ++source_p;
 345         quote_seen = 0;
 346         for (; 1; source_p++) {
 347                 switch (GET_CHAR()) {
 348                 case nul_char:
 349                         append_string(block_start,
 350                                       &string,
 351                                       source_p - block_start);
 352                         GET_NEXT_BLOCK_NOCHK(source);
 353                         if (source == NULL) {
 354                                 if (current_string != NULL) {
 355                                         WCSTOMBS(mbs_buffer, current_string);
 356                                         fatal_reader_mksh(gettext("Unmatched `%c' in string `%s'"),
 357                                                      closer ==
 358                                                      (int) braceright_char ?
 359                                                      (int) braceleft_char :
 360                                                      (int) parenleft_char,
 361                                                      mbs_buffer);
 362                                 } else {
 363                                         fatal_reader_mksh(gettext("Premature EOF"));
 364                                 }
 365                         }
 366                         if (source->error_converting) {
 367                                 fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()");
 368                         }
 369                         block_start = source_p;
 370                         source_p--;
 371                         continue;
 372                 case newline_char:
 373                         fatal_reader_mksh(gettext("Unmatched `%c' on line"),
 374                                      closer == (int) braceright_char ?
 375                                      (int) braceleft_char :
 376                                      (int) parenleft_char);
 377                 case backslash_char:
 378                         /* Quote dollar in macro value. */
 379                         if (!cmd) {
 380                                 quote_seen = ~quote_seen;
 381                         }
 382                         continue;
 383                 case dollar_char:
 384                         /*
 385                          * Macro names may reference macros.
 386                          * This expands the value of such macros into the
 387                          * macro name string.
 388                          */
 389                         if (quote_seen) {
 390                                 append_string(block_start,
 391                                               &string,
 392                                               source_p - block_start - 1);
 393                                 block_start = source_p;
 394                                 break;
 395                         }
 396                         append_string(block_start,
 397                                       &string,
 398                                       source_p - block_start);
 399                         source->string.text.p = ++source_p;
 400                         UNCACHE_SOURCE();
 401                         expand_macro(source, &string, current_string, cmd);
 402                         CACHE_SOURCE(0);
 403                         block_start = source_p;
 404                         source_p--;
 405                         break;
 406                 case parenleft_char:
 407                         /* Allow nested pairs of () in the macro name. */
 408                         if (closer == (int) parenright_char) {
 409                                 closer_level++;
 410                         }
 411                         break;
 412                 case braceleft_char:
 413                         /* Allow nested pairs of {} in the macro name. */
 414                         if (closer == (int) braceright_char) {
 415                                 closer_level++;
 416                         }
 417                         break;
 418                 case parenright_char:
 419                 case braceright_char:
 420                         /*
 421                          * End of the name. Save the string in the macro
 422                          * name string.
 423                          */
 424                         if ((*source_p == closer) && (--closer_level <= 0)) {
 425                                 source->string.text.p = source_p + 1;
 426                                 append_string(block_start,
 427                                               &string,
 428                                               source_p - block_start);
 429                                 goto get_macro_value;
 430                         }
 431                         break;
 432                 }
 433                 quote_seen = 0;
 434         }
 435         /*
 436          * We got the macro name. We now inspect it to see if it
 437          * specifies any translations of the value.
 438          */
 439 get_macro_value:
 440         name = NULL;
 441         /* First check if we have a $(@D) type translation. */
 442         if ((get_char_semantics_value(string.buffer.start[0]) &
 443              (int) special_macro_sem) &&
 444             (string.text.p - string.buffer.start >= 2) &&
 445             ((string.buffer.start[1] == 'D') ||
 446              (string.buffer.start[1] == 'F'))) {
 447                 switch (string.buffer.start[1]) {
 448                 case 'D':
 449                         extraction = dir_extract;
 450                         break;
 451                 case 'F':
 452                         extraction = file_extract;
 453                         break;
 454                 default:
 455                         WCSTOMBS(mbs_buffer, string.buffer.start);
 456                         fatal_reader_mksh(gettext("Illegal macro reference `%s'"),
 457                                      mbs_buffer);
 458                 }
 459                 /* Internalize the macro name using the first char only. */
 460                 name = GETNAME(string.buffer.start, 1);
 461                 (void) wcscpy(string.buffer.start, string.buffer.start + 2);
 462         }
 463         /* Check for other kinds of translations. */
 464         if ((colon = (wchar_t *) wcschr(string.buffer.start,
 465                                        (int) colon_char)) != NULL) {
 466                 /*
 467                  * We have a $(FOO:.c=.o) type translation.
 468                  * Get the name of the macro proper.
 469                  */
 470                 if (name == NULL) {
 471                         name = GETNAME(string.buffer.start,
 472                                        colon - string.buffer.start);
 473                 }
 474                 /* Pickup all the translations. */
 475                 if (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell)) {
 476                         replacement = sh_replace;
 477                 } else if ((svr4) ||
 478                            ((percent = (wchar_t *) wcschr(colon + 1,
 479                                                          (int) percent_char)) == NULL)) {
 480                         while (colon != NULL) {
 481                                 if ((eq = (wchar_t *) wcschr(colon + 1,
 482                                                             (int) equal_char)) == NULL) {
 483                                         fatal_reader_mksh(gettext("= missing from replacement macro reference"));
 484                                 }
 485                                 left_tail_len = eq - colon - 1;
 486                                 if(left_tail) {
 487                                         retmem(left_tail);
 488                                 }
 489                                 left_tail = ALLOC_WC(left_tail_len + 1);
 490                                 (void) wcsncpy(left_tail,
 491                                               colon + 1,
 492                                               eq - colon - 1);
 493                                 left_tail[eq - colon - 1] = (int) nul_char;
 494                                 replacement = suffix_replace;
 495                                 if ((colon = (wchar_t *) wcschr(eq + 1,
 496                                                                (int) colon_char)) != NULL) {
 497                                         tmp_len = colon - eq;
 498                                         if(right_tail) {
 499                                                 retmem(right_tail);
 500                                         }
 501                                         right_tail = ALLOC_WC(tmp_len);
 502                                         (void) wcsncpy(right_tail,
 503                                                       eq + 1,
 504                                                       colon - eq - 1);
 505                                         right_tail[colon - eq - 1] =
 506                                           (int) nul_char;
 507                                 } else {
 508                                         if(right_tail) {
 509                                                 retmem(right_tail);
 510                                         }
 511                                         right_tail = ALLOC_WC(wcslen(eq) + 1);
 512                                         (void) wcscpy(right_tail, eq + 1);
 513                                 }
 514                         }
 515                 } else {
 516                         if ((eq = (wchar_t *) wcschr(colon + 1,
 517                                                     (int) equal_char)) == NULL) {
 518                                 fatal_reader_mksh(gettext("= missing from replacement macro reference"));
 519                         }
 520                         if ((percent = (wchar_t *) wcschr(colon + 1,
 521                                                          (int) percent_char)) == NULL) {
 522                                 fatal_reader_mksh(gettext("%% missing from replacement macro reference"));
 523                         }
 524                         if (eq < percent) {
 525                                 fatal_reader_mksh(gettext("%% missing from replacement macro reference"));
 526                         }
 527 
 528                         if (percent > (colon + 1)) {
 529                                 tmp_len = percent - colon;
 530                                 if(left_head) {
 531                                         retmem(left_head);
 532                                 }
 533                                 left_head = ALLOC_WC(tmp_len);
 534                                 (void) wcsncpy(left_head,
 535                                               colon + 1,
 536                                               percent - colon - 1);
 537                                 left_head[percent-colon-1] = (int) nul_char;
 538                                 left_head_len = percent-colon-1;
 539                         } else {
 540                                 left_head = NULL;
 541                                 left_head_len = 0;
 542                         }
 543 
 544                         if (eq > percent+1) {
 545                                 tmp_len = eq - percent;
 546                                 if(left_tail) {
 547                                         retmem(left_tail);
 548                                 }
 549                                 left_tail = ALLOC_WC(tmp_len);
 550                                 (void) wcsncpy(left_tail,
 551                                               percent + 1,
 552                                               eq - percent - 1);
 553                                 left_tail[eq-percent-1] = (int) nul_char;
 554                                 left_tail_len = eq-percent-1;
 555                         } else {
 556                                 left_tail = NULL;
 557                                 left_tail_len = 0;
 558                         }
 559 
 560                         if ((percent = (wchar_t *) wcschr(++eq,
 561                                                          (int) percent_char)) == NULL) {
 562 
 563                                 right_hand[0] = ALLOC_WC(wcslen(eq) + 1);
 564                                 right_hand[1] = NULL;
 565                                 (void) wcscpy(right_hand[0], eq);
 566                         } else {
 567                                 i = 0;
 568                                 do {
 569                                         right_hand[i] = ALLOC_WC(percent-eq+1);
 570                                         (void) wcsncpy(right_hand[i],
 571                                                       eq,
 572                                                       percent - eq);
 573                                         right_hand[i][percent-eq] =
 574                                           (int) nul_char;
 575                                         if (i++ >= VSIZEOF(right_hand)) {
 576                                                 fatal_mksh(gettext("Too many %% in pattern"));
 577                                         }
 578                                         eq = percent + 1;
 579                                         if (eq[0] == (int) nul_char) {
 580                                                 MBSTOWCS(wcs_buffer, "");
 581                                                 right_hand[i] = (wchar_t *) wcsdup(wcs_buffer);
 582                                                 i++;
 583                                                 break;
 584                                         }
 585                                 } while ((percent = (wchar_t *) wcschr(eq, (int) percent_char)) != NULL);
 586                                 if (eq[0] != (int) nul_char) {
 587                                         right_hand[i] = ALLOC_WC(wcslen(eq) + 1);
 588                                         (void) wcscpy(right_hand[i], eq);
 589                                         i++;
 590                                 }
 591                                 right_hand[i] = NULL;
 592                         }
 593                         replacement = pattern_replace;
 594                 }
 595         }
 596         if (name == NULL) {
 597                 /*
 598                  * No translations found.
 599                  * Use the whole string as the macro name.
 600                  */
 601                 name = GETNAME(string.buffer.start,
 602                                string.text.p - string.buffer.start);
 603         }
 604         if (string.free_after_use) {
 605                 retmem(string.buffer.start);
 606         }
 607         if (name == make) {
 608                 make_word_mentioned = true;
 609         }
 610         if (name == query) {
 611                 query_mentioned = true;
 612         }
 613         if ((name == host_arch) || (name == target_arch)) {
 614                 if (!init_arch_done) {
 615                         init_arch_done = true;
 616                         init_arch_macros();
 617                 }
 618         }
 619         if ((name == host_mach) || (name == target_mach)) {
 620                 if (!init_mach_done) {
 621                         init_mach_done = true;
 622                         init_mach_macros();
 623                 }
 624         }
 625         /* Get the macro value. */
 626         macro = get_prop(name->prop, macro_prop);
 627         if ((macro != NULL) && macro->body.macro.is_conditional) {
 628                 conditional_macro_used = true;
 629                 /*
 630                  * Add this conditional macro to the beginning of the
 631                  * global list.
 632                  */
 633                 add_macro_to_global_list(name);
 634                 if (makefile_type == reading_makefile) {
 635                         warning_mksh(gettext("Conditional macro `%s' referenced in file `%ws', line %d"),
 636                                         name->string_mb, file_being_read, line_number);
 637                 }
 638         }
 639         /* Macro name read and parsed. Expand the value. */
 640         if ((macro == NULL) || (macro->body.macro.value == NULL)) {
 641                 /* If the value is empty, we just get out of here. */
 642                 goto exit;
 643         }
 644         if (replacement == sh_replace) {
 645                 /* If we should do a :sh transform, we expand the command
 646                  * and process it.
 647                  */
 648                 INIT_STRING_FROM_STACK(string, buffer);
 649                 /* Expand the value into a local string buffer and run cmd. */
 650                 expand_value_with_daemon(name, macro, &string, cmd);
 651                 sh_command2string(&string, destination);
 652         } else if ((replacement != no_replace) || (extraction != no_extract)) {
 653                 /*
 654                  * If there were any transforms specified in the macro
 655                  * name, we deal with them here.
 656                  */
 657                 INIT_STRING_FROM_STACK(string, buffer);
 658                 /* Expand the value into a local string buffer. */
 659                 expand_value_with_daemon(name, macro, &string, cmd);
 660                 /* Scan the expanded string. */
 661                 p = string.buffer.start;
 662                 while (*p != (int) nul_char) {
 663                         wchar_t         chr;
 664 
 665                         /*
 666                          * First skip over any white space and append
 667                          * that to the destination string.
 668                          */
 669                         block_start = p;
 670                         while ((*p != (int) nul_char) && iswspace(*p)) {
 671                                 p++;
 672                         }
 673                         append_string(block_start,
 674                                       destination,
 675                                       p - block_start);
 676                         /* Then find the end of the next word. */
 677                         block_start = p;
 678                         while ((*p != (int) nul_char) && !iswspace(*p)) {
 679                                 p++;
 680                         }
 681                         /* If we cant find another word we are done */
 682                         if (block_start == p) {
 683                                 break;
 684                         }
 685                         /* Then apply the transforms to the word */
 686                         INIT_STRING_FROM_STACK(extracted, extracted_string);
 687                         switch (extraction) {
 688                         case dir_extract:
 689                                 /*
 690                                  * $(@D) type transform. Extract the
 691                                  * path from the word. Deliver "." if
 692                                  * none is found.
 693                                  */
 694                                 if (p != NULL) {
 695                                         chr = *p;
 696                                         *p = (int) nul_char;
 697                                 }
 698                                 eq = (wchar_t *) wcsrchr(block_start, (int) slash_char);
 699                                 if (p != NULL) {
 700                                         *p = chr;
 701                                 }
 702                                 if ((eq == NULL) || (eq > p)) {
 703                                         MBSTOWCS(wcs_buffer, ".");
 704                                         append_string(wcs_buffer, &extracted, 1);
 705                                 } else {
 706                                         append_string(block_start,
 707                                                       &extracted,
 708                                                       eq - block_start);
 709                                 }
 710                                 break;
 711                         case file_extract:
 712                                 /*
 713                                  * $(@F) type transform. Remove the path
 714                                  * from the word if any.
 715                                  */
 716                                 if (p != NULL) {
 717                                         chr = *p;
 718                                         *p = (int) nul_char;
 719                                 }
 720                                 eq = (wchar_t *) wcsrchr(block_start, (int) slash_char);
 721                                 if (p != NULL) {
 722                                         *p = chr;
 723                                 }
 724                                 if ((eq == NULL) || (eq > p)) {
 725                                         append_string(block_start,
 726                                                       &extracted,
 727                                                       p - block_start);
 728                                 } else {
 729                                         append_string(eq + 1,
 730                                                       &extracted,
 731                                                       p - eq - 1);
 732                                 }
 733                                 break;
 734                         case no_extract:
 735                                 append_string(block_start,
 736                                               &extracted,
 737                                               p - block_start);
 738                                 break;
 739                         }
 740                         switch (replacement) {
 741                         case suffix_replace:
 742                                 /*
 743                                  * $(FOO:.o=.c) type transform.
 744                                  * Maybe replace the tail of the word.
 745                                  */
 746                                 if (((extracted.text.p -
 747                                       extracted.buffer.start) >=
 748                                      left_tail_len) &&
 749                                     IS_WEQUALN(extracted.text.p - left_tail_len,
 750                                               left_tail,
 751                                               left_tail_len)) {
 752                                         append_string(extracted.buffer.start,
 753                                                       destination,
 754                                                       (extracted.text.p -
 755                                                        extracted.buffer.start)
 756                                                       - left_tail_len);
 757                                         append_string(right_tail,
 758                                                       destination,
 759                                                       FIND_LENGTH);
 760                                 } else {
 761                                         append_string(extracted.buffer.start,
 762                                                       destination,
 763                                                       FIND_LENGTH);
 764                                 }
 765                                 break;
 766                         case pattern_replace:
 767                                 /* $(X:a%b=c%d) type transform. */
 768                                 if (((extracted.text.p -
 769                                       extracted.buffer.start) >=
 770                                      left_head_len+left_tail_len) &&
 771                                     IS_WEQUALN(left_head,
 772                                               extracted.buffer.start,
 773                                               left_head_len) &&
 774                                     IS_WEQUALN(left_tail,
 775                                               extracted.text.p - left_tail_len,
 776                                               left_tail_len)) {
 777                                         i = 0;
 778                                         while (right_hand[i] != NULL) {
 779                                                 append_string(right_hand[i],
 780                                                               destination,
 781                                                               FIND_LENGTH);
 782                                                 i++;
 783                                                 if (right_hand[i] != NULL) {
 784                                                         append_string(extracted.buffer.
 785                                                                       start +
 786                                                                       left_head_len,
 787                                                                       destination,
 788                                                                       (extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len);
 789                                                 }
 790                                         }
 791                                 } else {
 792                                         append_string(extracted.buffer.start,
 793                                                       destination,
 794                                                       FIND_LENGTH);
 795                                 }
 796                                 break;
 797                         case no_replace:
 798                                 append_string(extracted.buffer.start,
 799                                               destination,
 800                                               FIND_LENGTH);
 801                                 break;
 802                         case sh_replace:
 803                                 break;
 804                             }
 805                 }
 806                 if (string.free_after_use) {
 807                         retmem(string.buffer.start);
 808                 }
 809         } else {
 810                 /*
 811                  * This is for the case when the macro name did not
 812                  * specify transforms.
 813                  */
 814                 if (!strncmp(name->string_mb, "GET", 3)) {
 815                         dollarget_seen = true;
 816                 }
 817                 dollarless_flag = false;
 818                 if (!strncmp(name->string_mb, "<", 1) &&
 819                     dollarget_seen) {
 820                         dollarless_flag = true;
 821                         dollarget_seen = false;
 822                 }
 823                 expand_value_with_daemon(name, macro, destination, cmd);
 824         }
 825 exit:
 826         if(left_tail) {
 827                 retmem(left_tail);
 828         }
 829         if(right_tail) {
 830                 retmem(right_tail);
 831         }
 832         if(left_head) {
 833                 retmem(left_head);
 834         }
 835         i = 0;
 836         while (right_hand[i] != NULL) {
 837                 retmem(right_hand[i]);
 838                 i++;
 839         }
 840         *destination->text.p = (int) nul_char;
 841         destination->text.end = destination->text.p;
 842 }
 843 
 844 static void
 845 add_macro_to_global_list(Name macro_to_add)
 846 {
 847         Macro_list      new_macro;
 848         Macro_list      macro_on_list;
 849         char            *name_on_list = (char*)NULL;
 850         char            *name_to_add = macro_to_add->string_mb;
 851         char            *value_on_list = (char*)NULL;
 852         const char      *value_to_add = (char*)NULL;
 853 
 854         if (macro_to_add->prop->body.macro.value != NULL) {
 855                 value_to_add = macro_to_add->prop->body.macro.value->string_mb;
 856         } else {
 857                 value_to_add = "";
 858         }
 859 
 860         /* 
 861          * Check if this macro is already on list, if so, do nothing
 862          */
 863         for (macro_on_list = cond_macro_list;
 864              macro_on_list != NULL;
 865              macro_on_list = macro_on_list->next) {
 866 
 867                 name_on_list = macro_on_list->macro_name;
 868                 value_on_list = macro_on_list->value;
 869 
 870                 if (IS_EQUAL(name_on_list, name_to_add)) {
 871                         if (IS_EQUAL(value_on_list, value_to_add)) {
 872                                 return;
 873                         }
 874                 }
 875         }
 876         new_macro = (Macro_list) malloc(sizeof(Macro_list_rec));
 877         new_macro->macro_name = strdup(name_to_add);
 878         new_macro->value = strdup(value_to_add);
 879         new_macro->next = cond_macro_list;
 880         cond_macro_list = new_macro;
 881 }
 882 
 883 /*
 884  *      init_arch_macros(void)
 885  *
 886  *      Set the magic macros TARGET_ARCH, HOST_ARCH,
 887  *
 888  *      Parameters: 
 889  *
 890  *      Global variables used:
 891  *                              host_arch   Property for magic macro HOST_ARCH
 892  *                              target_arch Property for magic macro TARGET_ARCH
 893  *
 894  *      Return value:
 895  *                              The function does not return a value, but can
 896  *                              call fatal() in case of error.
 897  */
 898 static void
 899 init_arch_macros(void)
 900 {
 901         String_rec      result_string;
 902         wchar_t         wc_buf[STRING_BUFFER_LENGTH];
 903         char            mb_buf[STRING_BUFFER_LENGTH];
 904         FILE            *pipe;
 905         Name            value;
 906         int             set_host, set_target;
 907         const char      *mach_command = "/bin/mach";
 908 
 909         set_host = (get_prop(host_arch->prop, macro_prop) == NULL);
 910         set_target = (get_prop(target_arch->prop, macro_prop) == NULL);
 911 
 912         if (set_host || set_target) {
 913                 INIT_STRING_FROM_STACK(result_string, wc_buf);
 914                 append_char((int) hyphen_char, &result_string);
 915 
 916                 if ((pipe = popen(mach_command, "r")) == NULL) {
 917                         fatal_mksh(gettext("Execute of %s failed"), mach_command);
 918                 }
 919                 while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) {
 920                         MBSTOWCS(wcs_buffer, mb_buf);
 921                         append_string(wcs_buffer, &result_string, wcslen(wcs_buffer));
 922                 }
 923                 if (pclose(pipe) != 0) {
 924                         fatal_mksh(gettext("Execute of %s failed"), mach_command);
 925                 }
 926 
 927                 value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start));
 928 
 929                 if (set_host) {
 930                         (void) setvar_daemon(host_arch, value, false, no_daemon, true, 0);
 931                 }
 932                 if (set_target) {
 933                         (void) setvar_daemon(target_arch, value, false, no_daemon, true, 0);
 934                 }
 935         }
 936 }
 937 
 938 /*
 939  *      init_mach_macros(void)
 940  *
 941  *      Set the magic macros TARGET_MACH, HOST_MACH,
 942  *
 943  *      Parameters: 
 944  *
 945  *      Global variables used:
 946  *                              host_mach   Property for magic macro HOST_MACH
 947  *                              target_mach Property for magic macro TARGET_MACH
 948  *
 949  *      Return value:
 950  *                              The function does not return a value, but can
 951  *                              call fatal() in case of error.
 952  */
 953 static void
 954 init_mach_macros(void)
 955 {
 956         String_rec      result_string;
 957         wchar_t         wc_buf[STRING_BUFFER_LENGTH];
 958         char            mb_buf[STRING_BUFFER_LENGTH];
 959         FILE            *pipe;
 960         Name            value;
 961         int             set_host, set_target;
 962         const char      *arch_command = "/bin/arch";
 963 
 964         set_host = (get_prop(host_mach->prop, macro_prop) == NULL);
 965         set_target = (get_prop(target_mach->prop, macro_prop) == NULL);
 966 
 967         if (set_host || set_target) {
 968                 INIT_STRING_FROM_STACK(result_string, wc_buf);
 969                 append_char((int) hyphen_char, &result_string);
 970 
 971                 if ((pipe = popen(arch_command, "r")) == NULL) {
 972                         fatal_mksh(gettext("Execute of %s failed"), arch_command);
 973                 }
 974                 while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) {
 975                         MBSTOWCS(wcs_buffer, mb_buf);
 976                         append_string(wcs_buffer, &result_string, wcslen(wcs_buffer));
 977                 }
 978                 if (pclose(pipe) != 0) {
 979                         fatal_mksh(gettext("Execute of %s failed"), arch_command);
 980                 }
 981 
 982                 value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start));
 983 
 984                 if (set_host) {
 985                         (void) setvar_daemon(host_mach, value, false, no_daemon, true, 0);
 986                 }
 987                 if (set_target) {
 988                         (void) setvar_daemon(target_mach, value, false, no_daemon, true, 0);
 989                 }
 990         }
 991 }
 992 
 993 /*
 994  *      expand_value_with_daemon(name, macro, destination, cmd)
 995  *
 996  *      Checks for daemons and then maybe calls expand_value().
 997  *
 998  *      Parameters:
 999  *              name            Name of the macro  (Added by the NSE)
1000  *              macro           The property block with the value to expand
1001  *              destination     Where the result should be deposited
1002  *              cmd             If we are evaluating a command line we
1003  *                              turn \ quoting off
1004  *
1005  *      Global variables used:
1006  */
1007 static void
1008 expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd)
1009 {
1010         register Chain          chain;
1011 
1012 
1013         switch (macro->body.macro.daemon) {
1014         case no_daemon:
1015                 if (!svr4 && !posix) {
1016                         expand_value(macro->body.macro.value, destination, cmd);
1017                 } else {
1018                         if (dollarless_flag && tilde_rule) {
1019                                 expand_value(dollarless_value, destination, cmd);
1020                                 dollarless_flag = false;
1021                                 tilde_rule = false;
1022                         } else {
1023                                 expand_value(macro->body.macro.value, destination, cmd);
1024                         }
1025                 }
1026                 return;
1027         case chain_daemon:
1028                 /* If this is a $? value we call the daemon to translate the */
1029                 /* list of names to a string */
1030                 for (chain = (Chain) macro->body.macro.value;
1031                      chain != NULL;
1032                      chain = chain->next) {
1033                         APPEND_NAME(chain->name,
1034                                       destination,
1035                                       (int) chain->name->hash.length);
1036                         if (chain->next != NULL) {
1037                                 append_char((int) space_char, destination);
1038                         }
1039                 }
1040                 return;
1041         }
1042 }
1043 
1044 /*
1045  * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value.
1046  */
1047 char    *sunpro_dependencies_buf = NULL;
1048 char    *sunpro_dependencies_oldbuf = NULL;
1049 int     sunpro_dependencies_buf_size = 0;
1050 
1051 /*
1052  *      setvar_daemon(name, value, append, daemon, strip_trailing_spaces)
1053  *
1054  *      Set a macro value, possibly supplying a daemon to be used
1055  *      when referencing the value.
1056  *
1057  *      Return value:
1058  *                              The property block with the new value
1059  *
1060  *      Parameters:
1061  *              name            Name of the macro to set
1062  *              value           The value to set
1063  *              append          Should we reset or append to the current value?
1064  *              daemon          Special treatment when reading the value
1065  *              strip_trailing_spaces from the end of value->string
1066  *              debug_level     Indicates how much tracing we should do
1067  *
1068  *      Global variables used:
1069  *              makefile_type   Used to check if we should enforce read only
1070  *              path_name       The Name "PATH", compared against
1071  *              virtual_root    The Name "VIRTUAL_ROOT", compared against
1072  *              vpath_defined   Set if the macro VPATH is set
1073  *              vpath_name      The Name "VPATH", compared against
1074  *              envvar          A list of environment vars with $ in value
1075  */
1076 Property
1077 setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level)
1078 {
1079         register Property       macro = maybe_append_prop(name, macro_prop);
1080         register Property       macro_apx = get_prop(name->prop, macro_append_prop);
1081         int                     length = 0;
1082         String_rec              destination;
1083         wchar_t                 buffer[STRING_BUFFER_LENGTH];
1084         register Chain          chain;
1085         Name                    val;
1086         wchar_t                 *val_string = (wchar_t*)NULL;
1087         Wstring                 wcb;
1088 
1089 
1090         if ((makefile_type != reading_nothing) &&
1091             macro->body.macro.read_only) {
1092                 return macro;
1093         }
1094         /* Strip spaces from the end of the value */
1095         if (daemon == no_daemon) {
1096                 if(value != NULL) {
1097                         wcb.init(value);
1098                         length = wcb.length();
1099                         val_string = wcb.get_string();
1100                 }
1101                 if ((length > 0) && iswspace(val_string[length-1])) {
1102                         INIT_STRING_FROM_STACK(destination, buffer);
1103                         buffer[0] = 0;
1104                         append_string(val_string, &destination, length);
1105                         if (strip_trailing_spaces) {
1106                                 while ((length > 0) &&
1107                                        iswspace(destination.buffer.start[length-1])) {
1108                                         destination.buffer.start[--length] = 0;
1109                                 }
1110                         }
1111                         value = GETNAME(destination.buffer.start, FIND_LENGTH);
1112                 }
1113         }
1114                 
1115         if(macro_apx != NULL) {
1116                 val = macro_apx->body.macro_appendix.value;
1117         } else {
1118                 val = macro->body.macro.value;
1119         }
1120 
1121         if (append) {
1122                 /*
1123                  * If we are appending, we just tack the new value after
1124                  * the old one with a space in between.
1125                  */
1126                 INIT_STRING_FROM_STACK(destination, buffer);
1127                 buffer[0] = 0;
1128                 if ((macro != NULL) && (val != NULL)) {
1129                         APPEND_NAME(val,
1130                                       &destination,
1131                                       (int) val->hash.length);
1132                         if (value != NULL) {
1133                                 wcb.init(value);
1134                                 if(wcb.length() > 0) {
1135                                         MBTOWC(wcs_buffer, " ");
1136                                         append_char(wcs_buffer[0], &destination);
1137                                 }
1138                         }
1139                 }
1140                 if (value != NULL) {
1141                         APPEND_NAME(value,
1142                                       &destination,
1143                                       (int) value->hash.length);
1144                 }
1145                 value = GETNAME(destination.buffer.start, FIND_LENGTH);
1146                 wcb.init(value);
1147                 if (destination.free_after_use) {
1148                         retmem(destination.buffer.start);
1149                 }
1150         }
1151 
1152         /* Debugging trace */
1153         if (debug_level > 1) {
1154                 if (value != NULL) {
1155                         switch (daemon) {
1156                         case chain_daemon:
1157                                 (void) printf("%s =", name->string_mb);
1158                                 for (chain = (Chain) value;
1159                                      chain != NULL;
1160                                      chain = chain->next) {
1161                                         (void) printf(" %s", chain->name->string_mb);
1162                                 }
1163                                 (void) printf("\n");
1164                                 break;
1165                         case no_daemon:
1166                                 (void) printf("%s= %s\n",
1167                                               name->string_mb,
1168                                               value->string_mb);
1169                                 break;
1170                         }
1171                 } else {
1172                         (void) printf("%s =\n", name->string_mb);
1173                 }
1174         }
1175         /* Set the new values in the macro property block */
1176 /**/
1177         if(macro_apx != NULL) {
1178                 macro_apx->body.macro_appendix.value = value;
1179                 INIT_STRING_FROM_STACK(destination, buffer);
1180                 buffer[0] = 0;
1181                 if (value != NULL) {
1182                         APPEND_NAME(value,
1183                                       &destination,
1184                                       (int) value->hash.length);
1185                         if (macro_apx->body.macro_appendix.value_to_append != NULL) {
1186                                 MBTOWC(wcs_buffer, " ");
1187                                 append_char(wcs_buffer[0], &destination);
1188                         }
1189                 }
1190                 if (macro_apx->body.macro_appendix.value_to_append != NULL) {
1191                         APPEND_NAME(macro_apx->body.macro_appendix.value_to_append,
1192                                       &destination,
1193                                       (int) macro_apx->body.macro_appendix.value_to_append->hash.length);
1194                 }
1195                 value = GETNAME(destination.buffer.start, FIND_LENGTH);
1196                 if (destination.free_after_use) {
1197                         retmem(destination.buffer.start);
1198                 }
1199         }
1200 /**/
1201         macro->body.macro.value = value;
1202         macro->body.macro.daemon = daemon;
1203         /*
1204          * If the user changes the VIRTUAL_ROOT, we need to flush
1205          * the vroot package cache.
1206          */
1207         if (name == path_name) {
1208                 flush_path_cache();
1209         }
1210         if (name == virtual_root) {
1211                 flush_vroot_cache();
1212         }
1213         /* If this sets the VPATH we remember that */
1214         if ((name == vpath_name) &&
1215             (value != NULL) &&
1216             (value->hash.length > 0)) {
1217                 vpath_defined = true;
1218         }
1219         /*
1220          * For environment variables we also set the
1221          * environment value each time.
1222          */
1223         if (macro->body.macro.exported) {
1224                 static char     *env;
1225 
1226                 if (!reading_environment && (value != NULL)) {
1227                         Envvar  p;
1228 
1229                         for (p = envvar; p != NULL; p = p->next) {
1230                                 if (p->name == name) {
1231                                         p->value = value;
1232                                         p->already_put = false;
1233                                         goto found_it;
1234                                 }
1235                         }
1236                         p = ALLOC(Envvar);
1237                         p->name = name;
1238                         p->value = value;
1239                         p->next = envvar;
1240                         p->env_string = NULL;
1241                         p->already_put = false;
1242                         envvar = p;
1243 found_it:;
1244                 } if (reading_environment || (value == NULL) || !value->dollar) {
1245                         length = 2 + strlen(name->string_mb);
1246                         if (value != NULL) {
1247                                 length += strlen(value->string_mb);
1248                         }
1249                         Property env_prop = maybe_append_prop(name, env_mem_prop);
1250                         /*
1251                          * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value.
1252                          */
1253                         if (!strncmp(name->string_mb, "SUNPRO_DEPENDENCIES", 19)) {
1254                                 if (length >= sunpro_dependencies_buf_size) {
1255                                         sunpro_dependencies_buf_size=length*2;
1256                                         if (sunpro_dependencies_buf_size < 4096)
1257                                                 sunpro_dependencies_buf_size = 4096; // Default minimum size
1258                                         if (sunpro_dependencies_buf)
1259                                                 sunpro_dependencies_oldbuf = sunpro_dependencies_buf;
1260                                         sunpro_dependencies_buf=getmem(sunpro_dependencies_buf_size);
1261                                 }
1262                                 env = sunpro_dependencies_buf;
1263                         } else {
1264                                 env = getmem(length);
1265                         }
1266                         env_alloc_num++;
1267                         env_alloc_bytes += length;
1268                         (void) sprintf(env,
1269                                        "%s=%s",
1270                                        name->string_mb,
1271                                        value == NULL ?
1272                                          "" : value->string_mb);
1273                         (void) putenv(env);
1274                         env_prop->body.env_mem.value = env;
1275                         if (sunpro_dependencies_oldbuf) {
1276                                 /* Return old buffer */
1277                                 retmem_mb(sunpro_dependencies_oldbuf);
1278                                 sunpro_dependencies_oldbuf = NULL;
1279                         }
1280                 }
1281         }
1282         if (name == target_arch) {
1283                 Name            ha = getvar(host_arch);
1284                 Name            ta = getvar(target_arch);
1285                 Name            vr = getvar(virtual_root);
1286                 int             length;
1287                 wchar_t         *new_value;
1288                 wchar_t         *old_vr;
1289                 Boolean         new_value_allocated = false;
1290 
1291                 Wstring         ha_str(ha);
1292                 Wstring         ta_str(ta);
1293                 Wstring         vr_str(vr);
1294 
1295                 wchar_t * wcb_ha = ha_str.get_string();
1296                 wchar_t * wcb_ta = ta_str.get_string();
1297                 wchar_t * wcb_vr = vr_str.get_string();
1298 
1299                 length = 32 +
1300                   wcslen(wcb_ha) +
1301                     wcslen(wcb_ta) +
1302                       wcslen(wcb_vr);
1303                 old_vr = wcb_vr;
1304                 MBSTOWCS(wcs_buffer, "/usr/arch/");
1305                 if (IS_WEQUALN(old_vr,
1306                                wcs_buffer,
1307                                wcslen(wcs_buffer))) {
1308                         old_vr = (wchar_t *) wcschr(old_vr, (int) colon_char) + 1;
1309                 }
1310                 if ( (ha == ta) || (wcslen(wcb_ta) == 0) ) {
1311                         new_value = old_vr;
1312                 } else {
1313                         new_value = ALLOC_WC(length);
1314                         new_value_allocated = true;
1315                         WCSTOMBS(mbs_buffer, old_vr);
1316                         (void) swprintf(new_value, length * SIZEOFWCHAR_T, 
1317                                         L"/usr/arch/%s/%s:%s",
1318                                         ha->string_mb + 1,
1319                                         ta->string_mb + 1,
1320                                         mbs_buffer);
1321                 }
1322                 if (new_value[0] != 0) {
1323                         (void) setvar_daemon(virtual_root,
1324                                              GETNAME(new_value, FIND_LENGTH),
1325                                              false,
1326                                              no_daemon,
1327                                              true,
1328                                              debug_level);
1329                 }
1330                 if (new_value_allocated) {
1331                         retmem(new_value);
1332                 }
1333         }
1334         return macro;
1335 }