[comp.os.vms] VMS Makefile part 6 of 8

awp8101@ritcv.UUCP (Andrew W. Potter) (02/09/88)

$Part6:
$ File_is="MKMACRO.C"
$ Check_Sum_is=1779878150
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X/*
XMAKE version 1.1, April 1987
XCopyright (C) 1987 by Jesse Perry.
XMAKE is in the public domain and may be freely distributed,
Xused, and modified, provided this notice is not removed.
X
Xmkmacro.c
XThis file contains the routines which handle macros, in particular
Xthe code to expand both named macros and special macros is here.
X*/
X
X#include "make.h"
X
Xchar *macro_instance();
X
X/* This routine looks for the named macro in the macro definition list.
XIt returns a pointer to the macro definition, if found, or NULL if the
Xname given doesn't match anything in the list. */
X
XMAKE_TARGET *
Xmacro_find(macname, maclist)
Xchar *macname;
Xregister MAKE_TARGET *maclist;
X{
X`009int cmp;
X
X`009cmp = 1;
X`009while (maclist != NULL && cmp > 0) {
X`009`009if ((cmp = strcmp(macname, maclist->tg_name->mt_text)) == 0) {
X`009`009`009return (maclist);
X`009`009}
X`009`009maclist = maclist->tg_next;
X`009}
X`009return (NULL);
X}
X
X/* Add a MACRO to the macro definition list.  Return TRUE for success. */
X
Xmacro_add(macptr, maclistptr)
Xregister MAKE_TARGET *macptr;
XMAKE_TARGET **maclistptr;
X{
X`009register MAKE_TOKEN *defptr;
X`009register MAKE_TARGET *maclist, *prevptr;
X`009int cmp;
X
X`009/* Scan through sorted macro list to find place to insert. */
X
X`009cmp = -1;
X`009prevptr = NULL;
X`009maclist = *maclistptr;
X`009while (maclist != NULL && (cmp = strcmp(macptr->tg_name->mt_text,
X`009    maclist->tg_name->mt_text)) > 0) {
X`009`009prevptr = maclist;
X`009`009maclist = maclist->tg_next;
X`009}
X
X`009/* Check for duplicate addition.  If macro is already defined,
X`009return success -- this allows redefinition of macros (e.g.,
X`009DEBUG) on the command line or in the default rules file. */
X
X`009if (cmp == 0) {
X`009`009return (TRUE);
X`009}
X
X`009/* Get length of macro definition. */
X
X`009macptr->tg_ndep = 0;
X`009defptr = macptr->tg_dep;
X`009while (defptr != NULL) {
X`009`009macptr->tg_ndep++;
X`009`009defptr = defptr->mt_next;
X`009}
X
X`009/* Insert MACRO in list. */
X
X`009if (prevptr == NULL) {
X`009`009macptr->tg_next = *maclistptr;
X`009`009*maclistptr = macptr;
X`009} else {
X`009`009macptr->tg_next = prevptr->tg_next;
X`009`009prevptr->tg_next = macptr;
X`009}
X`009return (TRUE);
X}
X
X/* Expand any named macros in a command line. */
X
Xcmd_expand(cmdptr, maclist)
XMAKE_COMMAND *cmdptr;
XMAKE_TARGET *maclist;
X{
X`009int retval;
X`009MAKE_COMMAND *next_cmd;
X
X`009retval = EXP_SIMPLE;
X`009while (cmdptr != NULL) {
X`009`009if (macro_expand(cmdptr->cmd_word, maclist) == EXP_ERROR) {
X`009`009`009retval = EXP_ERROR;
X`009`009}
X`009`009cmdptr = cmdptr->cmd_next;
X`009}
X#ifdef NAME_EXP_DEBUG
X`009if (retval == EXP_ERROR) {
X`009`009printf("[cmd_expand] Returning error.\n");
X`009}
X#endif
X`009return (retval);
X}
X
X/* Expand any named macros in the text of each token in the list given. */
X
Xmacro_expand(tokptr, maclist)
XMAKE_TOKEN *tokptr;
XMAKE_TARGET *maclist;
X{
X`009int retval;
X`009MAKE_TOKEN *next_token;
X
X`009retval = EXP_SIMPLE;
X`009while (tokptr != NULL) {
X`009`009next_token = tokptr->mt_next;
X`009`009if (token_expand(tokptr, maclist) == EXP_ERROR) {
X`009`009`009retval = EXP_ERROR;
X`009`009}
X`009`009tokptr = next_token;
X`009}
X#ifdef NAME_EXP_DEBUG
X`009if (retval == EXP_ERROR) {
X`009`009printf("[macro_expand] Returning error.\n");
X`009}
X#endif
X`009return (retval);
X}
X
X/* This routine scans a token for named macro invocations.  It expands any
Xwhich it finds and converts the single token passed into a list of tokens,
Xeach of which is a simple string produced by one instance of the expansion.
XNOTE:  this routine does NOT expand the special macros for the current
Xtarget or the current dependency list; it only expands invocations of NAMED
Xmacros.  Example:
X  If the input token is "$(DISK)\:[dir]$,(MOO).obj", and DISK is a macro
X  defined as "user4", and MOO is a macro defined as "xx yy zz", then the
X  output tokens produced by macro expansion are:
X  "user4\:[dir]xx.obj,", "user4\:[dir]yy.obj,", and "user4\:[dir]zz.obj"
X  Notice that the last token does not have a trailing comma.
XThe return value is EXP_SIMPLE if every token in the expansion is simple
Xtext, EXP_SPECIAL if any token contains a special macro, or EXP_ERROR if
Xany error occurs during expansion.
X*/
X
Xstatic
Xtoken_expand(tokptr, maclist)
XMAKE_TOKEN *tokptr;
XMAKE_TARGET *maclist;
X{
X`009int any_special, n_item, n_instance, orig_line, sepchr;
X`009char *iptr;
X`009MAKE_TOKEN *orig_next;
X`009MACRO_ITEM item_list[MAX_EXP_INSTANCE];
X
X`009if (tokptr->mt_text == NULL || tokptr->mt_simple) {
X`009`009return (EXP_SIMPLE);
X`009}
X
X#ifdef NAME_EXP_DEBUG
Xprintf("[token_expand] Expanding '%s'\n", tokptr->mt_text);
X#endif
X
X`009n_item = create_item(tokptr, maclist, item_list,
X`009    sizeof(item_list) / sizeof(item_list[0]), &n_instance,
X`009    &sepchr, &any_special);
X`009if (n_item < 0 || n_instance < 0) {
X#ifdef NAME_EXP_DEBUG
Xprintf("[token_expand] Returning error, n_item = %d, n_instance = %d.\n",
Xn_item, n_instance);
X#endif
X`009`009return (EXP_ERROR);
X`009}
X
X`009/* Check for empty macro.  THIS IS ONLY A TEMPORARY HACK, RIGHT? */
X
X`009if (n_item == 0 || n_instance == 0) {
X`009`009*tokptr->mt_text = '\0';
X`009`009tokptr->mt_simple = TRUE;
X`009`009return (EXP_NONE);
X`009}
X
X#ifdef NAME_EXP_DEBUG
Xprintf("[token_expand] %d item%s, %d instance%s.\n", n_item,
XPLURAL(n_item), n_instance, PLURAL(n_instance));
X#endif
X
X`009/* Create each instance of the expansion. */
X
X`009free(tokptr->mt_text);
X`009orig_line = tokptr->mt_line;
X`009tokptr->mt_simple = TRUE;
X`009tokptr->mt_text = macro_instance(item_list, n_item, sepchr);
X#ifdef NAME_EXP_DEBUG
Xprintf("    %s\n", tokptr->mt_text);
X#endif
X`009if (--n_instance <= 0) {
X`009`009return (EXP_SIMPLE);
X`009}
X`009orig_next = tokptr->mt_next;
X`009while (n_instance-- > 0) {
X`009`009if (n_instance == 0) {
X`009`009`009sepchr = '\0';
X`009`009}
X`009`009tokptr->mt_next = E_ALLOC(MAKE_TOKEN);
X`009`009tokptr = tokptr->mt_next;
X`009`009tokptr->mt_text = macro_instance(item_list, n_item, sepchr);
X`009`009tokptr->mt_next = NULL;
X`009`009tokptr->mt_line = orig_line;
X`009`009tokptr->mt_simple = TRUE;
X#ifdef NAME_EXP_DEBUG
Xprintf("    %s\n", tokptr->mt_text);
X#endif
X`009}
X`009tokptr->mt_next = orig_next;
X
X`009free_item(item_list, n_item);
X`009return (any_special ? EXP_SPECIAL : EXP_SIMPLE);
X}
X
X/* Parse the string to be expanded, which is pointed to by expstr.  Create
Xthe expansion item list using the macro definitions in maclist.  Return
Xthe number of items in the expansion list, or -1 if an error is detected. */
X
Xstatic
Xcreate_item(tokptr, maclist, item_list, max_item, ninstptr, sepptr, specptr)
XMAKE_TOKEN *tokptr;
XMAKE_TARGET *maclist;
XMACRO_ITEM *item_list;
Xint max_item;
Xint *ninstptr, *sepptr, *specptr;
X{
X`009int fill_len, n_item, n_instance, nmstat;
X`009int sep_chk, separator;
X`009char *expstr;
X`009MAKE_TARGET *macptr;
X`009char macro_name[MACRO_NAME_LEN + 1];
X`009char filler[MAX_EXP_INSTANCE + 1];
X
X`009/* Return 0 expansion items if there is no string to expand. */
X
X`009*ninstptr = 0;
X`009*sepptr = '\0';
X`009*specptr = FALSE;
X`009if (tokptr == NULL || (expstr = tokptr->mt_text) == NULL) {
X`009`009return (0);
X`009}
X
X`009/* Break input string into "items".  Each item is either a
X`009string of ordinary text or an invocation of a named macro.
X`009If it is a named macro, the corresponding item is the list
X`009of tokens which are the macro's definition. */
X
X`009n_instance = 0;
X`009n_item = 0;
X`009while (*expstr) {
X
X`009`009/* Check for item list overflow. */
X
X`009`009if (n_item >= max_item) {
V`009`009`009printf("FATAL ERROR:  item list overflow in expansion of '%s', line
X %d.\n",
X`009`009`009    tokptr->mt_text, tokptr->mt_line);
X`009`009`009make_exit(NORMAL_EXIT);
X`009`009}
X
X`009`009/* Get next (possibly empty) string of ordinary text. */
X
X`009`009fill_len = get_filler(&expstr, filler,
X`009`009    sizeof(filler) - 1, FALSE);
X`009`009if (fill_len > 0) {
X`009`009`009item_list[n_item].it_text = newstr(filler, fill_len);
X`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X`009`009`009n_item++;
X`009`009`009if (n_instance < 1) {
X`009`009`009`009n_instance = 1;
X`009`009`009}
X#ifdef NAME_EXP_DEBUG
Xprintf("  Item %d is '%s'\n", n_item, filler);
X#endif
X`009`009}
X
X`009`009/* Check current character. */
X
X`009`009if (*expstr == '\0') {
X`009`009`009break;
X`009`009} else if (*expstr != INVOKE_MACRO) {
X`009`009`009continue;`009/* get another filler string */
X`009`009}
X`009`009expstr++;`009/* skip past INVOKE_MACRO character */
X
X`009`009/* Scan to OPEN_DELIM, saving the separator
X`009`009character, if one is given. */
X
X`009`009sep_chk = get_delim(&expstr, sepptr, tokptr);
X`009`009if (sep_chk < 0) {
X`009`009`009free_item(item_list, n_item);
X#ifdef NAME_EXP_DEBUG
Xprintf("[create_item] Error from get_delim().\n");
X#endif
X`009`009`009return (-1);`009/* error in named macro */
X`009`009} else if (sep_chk > 0) {
X`009`009`009*specptr = TRUE;
X`009`009`009continue;`009/* special macro used */
X`009`009}
X
X`009`009/* Get name of macro.  The first character
X`009`009of the name is pointed to by expstr. */
X
X`009`009nmstat = get_macro_name(&expstr, macro_name,
X`009`009    sizeof(macro_name) - 1);
X`009`009if (nmstat < 0) {
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_chrequired, 5, 1, &CLOSE_DELIM,
X`009`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009`009printf("  ** Missing '%c' in '%s', line %d.\n",
X`009`009`009    CLOSE_DELIM, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009`009free_item(item_list, n_item);
X`009`009`009return (-1);
X`009`009}
X
X`009`009/* Look up named macro. */
X
X`009`009macptr = macro_find(macro_name, maclist);
X`009`009if (macptr == NULL) {
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_unrecmac, 5, FAO_AD(macro_name),
X`009`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009`009printf("  ** Unknown macro name '%s' used in '%s', line %d.\n",
X`009`009`009    macro_name, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009`009free_item(item_list, n_item);
X`009`009`009return (-1);
X`009`009}
X
X`009`009/* Check macro definition length against n_instance. */
X
X`009`009if (macptr->tg_ndep == 0) {
X`009`009`009continue;`009/* skip empty macros */
X`009`009}
X`009`009if (n_instance > 1 && n_instance != macptr->tg_ndep) {
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_maclendiff, 6,
X`009`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line,
X`009`009`009    FAO_AD(macro_name), macptr->tg_ndep);
X#else
X`009`009`009printf("  ** Macro definition lengths differ in '%s', line %d.\n",
X`009`009`009    tokptr->mt_text, tokptr->mt_line);
X`009`009`009printf("       Detected at macro '%s' which has length %d.\n",
X`009`009`009    macro_name, macptr->tg_ndep);
X#endif
X`009`009`009free_item(item_list, n_item);
X`009`009`009return (-1);
X`009`009}
X`009`009if (macptr->tg_ndep > n_instance) {
X`009`009`009n_instance = macptr->tg_ndep;
X`009`009}
X
X`009`009/* Save pointer to macro definition as item. */
X
X`009`009if (n_instance <= 1) {
X`009`009`009item_list[n_item].it_text = newstr(macptr->tg_dep->mt_text, -1);
X`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X`009`009`009if (n_instance < 1) {
X`009`009`009`009n_instance = 1;
X`009`009`009}
X#ifdef NAME_EXP_DEBUG
Xprintf("  Item %d is '%s'\n", n_item + 1, item_list[n_item].it_text);
X#endif
X`009`009} else {
X`009`009`009item_list[n_item].it_type = ITEM_TOKEN;
X`009`009`009item_list[n_item].it_token = macptr->tg_dep;
X#ifdef NAME_EXP_DEBUG
Xprintf("  Item %d is: ", n_item + 1);
Xshow_token(item_list[n_item].it_token, FALSE);
Xputchar('\n');
X#endif
X`009`009}
X`009`009n_item++;
X`009}
X`009if (n_instance <= 1) {
X`009`009*sepptr = '\0';
X`009}
X#ifdef NAME_EXP_DEBUG
Xif (*sepptr) {
Xprintf("  Separator is '%c'\n", *sepptr);
X} else {
Xprintf("  No separator character.\n");
X}
X#endif
X`009*ninstptr = n_instance;
X`009return (n_item);
X}
X
X/* Expand all special macros in a list of command lines.
XReturn EXP_SIMPLE for success, EXP_ERROR for error. */
X
Xcmd_exp_special(cmdptr, target, deplist)
XMAKE_COMMAND *cmdptr;
XMAKE_TOKEN *target, *deplist;
X{
X`009int retval;
X
X`009retval = EXP_SIMPLE;
X`009while (cmdptr != NULL) {
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cmd_exp_special] Expanding command line:\n");
Xshow_token(cmdptr->cmd_word, FALSE);
Xputchar('\n');
X#endif
X`009`009if (expand_special(cmdptr->cmd_word,
X`009`009    target, deplist) == EXP_ERROR) {
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cmd_exp_special] Error during expansion.\n");
X#endif
X`009`009`009retval = EXP_ERROR;
X`009`009}
X`009`009cmdptr = cmdptr->cmd_next;
X`009}
X`009return (retval);
X}
X
V/* Expand all special macros in a list of tokens.  Return EXP_SIMPLE if there a
Xre
Vno more special macros in any token in the list, EXP_SPECIAL if any macro in th
Xe
Xlist has an unexpanded special macro, or EXP_ERROR for error. */
X
Xexpand_special(tokptr, tname, deplist)
XMAKE_TOKEN *tokptr, *tname, *deplist;
X{
X`009int any_special, n_item, n_instance, orig_line, retval, sepchr;
X`009MAKE_TOKEN *orig_list, *orig_next;
X`009MACRO_ITEM item_list[MAX_EXP_INSTANCE];
X
X`009retval = EXP_SIMPLE;
X`009orig_list = tokptr;
X`009while (tokptr != NULL) {
X
X`009`009if (*tokptr->mt_text == '\0') {
X`009`009`009tokptr = tokptr->mt_next;
X`009`009`009continue;
X`009`009}
X
X`009`009orig_next = tokptr->mt_next;
X#ifdef SPEC_EXP_DEBUG
Xprintf("[expand_special] Expanding special macros in '%s'\n", tokptr->mt_text);
Xprintf("  (Rest of list is ");
Xshow_token(tokptr->mt_next, FALSE);
Xprintf(")\n");
X#endif
X`009`009n_item = cr_spec_item(tokptr, tname, deplist,
X`009`009    item_list, sizeof(item_list) / sizeof(item_list[0]),
X`009`009    &n_instance, &sepchr, &any_special);
X`009`009if (n_item < 0 || n_instance <= 0) {
X#ifdef SPEC_EXP_DEBUG
Xprintf("[expand_special] Returning error, n_item = %d, n_instance = %d.\n",
Xn_item, n_instance);
X#endif
X`009`009`009return (EXP_ERROR);
X`009`009}
X#ifdef SPEC_EXP_DEBUG
Xprintf("[expand_special] %d item%s, %d instance%s.\n", n_item,
XPLURAL(n_item), n_instance, PLURAL(n_instance));
X#endif
X
X`009`009/* Create each instance of the expansion. */
X
X`009`009free(tokptr->mt_text);
X`009`009orig_line = tokptr->mt_line;
X`009`009tokptr->mt_simple = TRUE;
X`009`009tokptr->mt_text = macro_instance(item_list, n_item, sepchr);
X#ifdef SPEC_EXP_DEBUG
Xprintf("    %s\n", tokptr->mt_text);
X#endif
X`009`009while (--n_instance > 0) {
X`009`009`009if (n_instance == 1) {
X`009`009`009`009sepchr = '\0';
X`009`009`009}
X`009`009`009tokptr->mt_next = E_ALLOC(MAKE_TOKEN);
X`009`009`009tokptr = tokptr->mt_next;
X`009`009`009tokptr->mt_text = macro_instance(item_list, n_item, sepchr);
X`009`009`009tokptr->mt_next = NULL;
X`009`009`009tokptr->mt_line = orig_line;
X`009`009`009tokptr->mt_simple = TRUE;
X#ifdef SPEC_EXP_DEBUG
Xprintf("    %s\n", tokptr->mt_text);
X#endif
X`009`009}
X`009`009tokptr->mt_next = orig_next;
X
X`009`009free_item(item_list, n_item);
X
X`009`009if (any_special) {
X`009`009`009retval = EXP_SPECIAL;
X`009`009}
X
X`009`009tokptr = orig_next;
X`009}
X`009return (retval);
X}
X
X/* Scan the token for special macros.  Create item list to expand these. */
X
Xstatic
Vcr_spec_item(tokptr, tname, deplist, item_list, max_item, ninstptr, sepptr, spe
Xcptr)
XMAKE_TOKEN *tokptr, *tname, *deplist;
XMACRO_ITEM *item_list;
Xint max_item;
Xint *ninstptr, *sepptr, *specptr;
X{
X`009int fill_len, n_item, n_instance, sep_chk, spec_chr, speclen;
X`009register char *expstr;
X`009MAKE_TOKEN *spptr;
X`009char filler[MAX_EXP_INSTANCE + 1];
X
X`009/* Return 0 expansion items if there is no string to expand. */
X
X`009*ninstptr = 0;
X`009*sepptr = '\0';
X`009*specptr = FALSE;
X`009if (tokptr == NULL || (expstr = tokptr->mt_text) == NULL) {
X`009`009return (0);
X`009}
X
X`009/* Break input string into "items".  Each item is either a
X`009string of ordinary text or an invocation of a special macro.
X`009If it is a special macro, the corresponding item is the list
X`009of tokens which are the macro's definition. */
X
X`009n_instance = 0;
X`009n_item = 0;
X`009while (*expstr) {
X
X`009`009/* Check for item list overflow. */
X
X`009`009if (n_item >= max_item) {
V`009`009`009printf("FATAL ERROR:  item list overflow in expansion of '%s', line
X %d.\n",
X`009`009`009    tokptr->mt_text, tokptr->mt_line);
X`009`009`009make_exit(NORMAL_EXIT);
X`009`009}
X
X`009`009/* Get next (possibly empty) string of ordinary text. */
X
X`009`009fill_len = get_filler(&expstr, filler,
X`009`009    sizeof(filler) - 1, TRUE);
X`009`009if (fill_len > 0) {
X`009`009`009item_list[n_item].it_text = newstr(filler, fill_len);
X`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X`009`009`009n_item++;
X`009`009`009if (n_instance < 1) {
X`009`009`009`009n_instance = 1;
X`009`009`009}
X#ifdef SPEC_EXP_DEBUG
Xprintf("  Item %d is '%s'\n", n_item, filler);
X#endif
X`009`009}
X
X`009`009/* Check current character. */
X
X`009`009if (*expstr == '\0') {
X`009`009`009break;
X`009`009} else if (*expstr != INVOKE_MACRO) {
X#ifdef SPEC_EXP_DEBUG
Vprintf("[cr_spec_item] Getting second filler string in a row, first char '%c'\n
X",
X*expstr);
X#endif
X`009`009`009continue;`009/* get another filler string */
X`009`009}
X`009`009expstr++;`009/* skip past INVOKE_MACRO character */
X
X`009`009/* Scan to special macro character, saving
X`009`009separator character, if one is given. */
X
X`009`009sep_chk = get_delim(&expstr, sepptr, tokptr);
X`009`009if (sep_chk < 0) {
X`009`009`009free_item(item_list, n_item);
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Error from get_delim().\n");
X#endif
X`009`009`009return (-1);`009/* error in named macro */
X`009`009} else if (sep_chk == 0) {
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Error; found a named macro in '%s'\n", tokptr->mt_text);
Xprintf("\tAll named macros should have been expanded earlier.\n");
X#endif
X`009`009`009continue;
X`009`009}
X
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Found use of special macro '%c' in '%s' on line %d.\n",
X*expstr, tokptr->mt_text, tokptr->mt_line);
X#endif
X
X`009`009/* Get special macro definition. */
X
X`009`009spec_chr = *expstr++;
X`009`009switch (spec_chr) {
X`009`009case CURRENT_TARGET:
X`009`009`009item_list[n_item].it_text = newstr(tname->mt_text, -1);
X`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Replacing %c%c by '%s'\n", INVOKE_MACRO, spec_chr,
Xitem_list[n_item].it_text);
X#endif
X`009`009`009n_item++;
X`009`009`009if (n_instance == 0) {
X`009`009`009`009n_instance = 1;
X`009`009`009}
X`009`009`009break;
X
X`009`009case CURR_TARG_NO_EXT:
X`009`009`009item_list[n_item].it_text = rm_file_type(tname->mt_text, NULL);
X`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Replacing %c%c by '%s'\n", INVOKE_MACRO, spec_chr,
Xitem_list[n_item].it_text);
X#endif
X`009`009`009n_item++;
X`009`009`009if (n_instance == 0) {
X`009`009`009`009n_instance = 1;
X`009`009`009}
X`009`009`009break;
X
X`009`009case DEPENDENCY_LIST:
X`009`009`009speclen = 0;
X`009`009`009spptr = deplist;
X`009`009`009while (spptr != NULL) {
X`009`009`009`009speclen++;
X`009`009`009`009spptr = spptr->mt_next;
X`009`009`009}
X
X`009`009`009if (speclen > 0) {
X`009`009`009`009if (speclen == 1) {
X`009`009`009`009`009item_list[n_item].it_text = newstr(deplist->mt_text, -1);
X`009`009`009`009`009item_list[n_item].it_type = ITEM_TEXT;
X#ifdef SPEC_EXP_DEBUG
Xprintf("[cr_spec_item] Replacing %c%c by '%s'\n", INVOKE_MACRO, spec_chr,
Xitem_list[n_item].it_text);
X#endif
X`009`009`009`009`009if (n_instance == 0) {
X`009`009`009`009`009`009n_instance = 1;
X`009`009`009`009`009}
X`009`009`009`009} else {
X`009`009`009`009`009item_list[n_item].it_token = deplist;
X`009`009`009`009`009item_list[n_item].it_type = ITEM_TOKEN;
X`009`009`009`009`009if (n_instance <= 1) {
X`009`009`009`009`009`009n_instance = speclen;
X`009`009`009`009`009} else if (n_instance != speclen) {
V`009`009`009`009`009`009printf("FATAL ERROR, Special macros have different defi
Xnition lengths in '%s'\n",
X`009`009`009`009`009`009    tokptr->mt_text);
X`009`009`009`009`009`009make_exit(NORMAL_EXIT);
X`009`009`009`009`009}
X`009`009`009`009}
X`009`009`009`009n_item++;
X`009`009`009}
X`009`009`009break;
X
X`009`009case OUT_OF_DATE_DEPS:
Xprintf("  ** $%c is unimplemented.\n", OUT_OF_DATE_DEPS);
X`009`009`009break;
X
X`009`009case UP_TO_DATE_DEPS:
Xprintf("  ** $%c is unimplemented.\n", UP_TO_DATE_DEPS);
X`009`009`009break;
X
X`009`009default:
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_unrecspec, 7,
X`009`009`009    1, &INVOKE_MACRO, 1, &spec_chr,
X`009`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009`009printf("  ** Unrecognized special macro %c%c in '%s', line %d.\n",
X`009`009`009    INVOKE_MACRO, spec_chr, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009}
X`009}
X`009if (n_instance <= 1) {
X`009`009*sepptr = '\0';
X`009}
X#ifdef SPEC_EXP_DEBUG
Xif (*sepptr) {
Xprintf("  Separator is '%c'\n", *sepptr);
X} else {
Xprintf("  No separator character.\n");
X}
X#endif
X`009*ninstptr = n_instance;
X`009return (n_item);
X}
X
X/* Free all space allocated for item_list. */
X
Xstatic
Xfree_item(itptr, n_item)
XMACRO_ITEM *itptr;
Xint n_item;
X{
X`009while (n_item-- > 0) {
X`009`009if (itptr->it_type == ITEM_TEXT && itptr->it_text != NULL) {
X`009`009`009free(itptr->it_text);
X`009`009`009itptr++;
X`009`009}
X`009}
X}
X
X/* strptr points to a pointer to the current position in the string being
Xexpanded.  This routine copies characters from the string into fillbuf,
Xstopping when a null or INVOKE_MACRO is found.  The number of characters
Xcopied is returned. */
X
Xstatic
Xget_filler(strptr, fillbuf, buflen, name_is_fill)
Xchar **strptr;
Xchar *fillbuf;
Xint buflen, name_is_fill;
X{
X`009register char *exp, *fillptr;
X`009int prev_literal, named_macro;
X
X`009prev_literal = FALSE;
X`009fillptr = fillbuf;
X`009exp = *strptr;
X
X`009/* This loop copies characters from the input string into the
X`009buffer given.  It stops when it finds the end of the input
X`009string, or the buffer is filled, or it finds a named macro
X`009invocation. */
X
X`009while (*exp && fillptr - fillbuf < buflen) {
X
X`009`009/* Check for any type of macro invocation. */
X
X`009`009if (*exp == INVOKE_MACRO && !prev_literal) {
X
X`009`009`009/* Check for a named macro invocation. */
X
X`009`009`009named_macro = (exp[1] == OPEN_DELIM ||
X`009`009`009    (!IS_SPECIAL(exp[1]) && exp[2] == OPEN_DELIM) ||
X`009`009`009    (exp[1] == NEXT_LITERAL && exp[3] == OPEN_DELIM));
X`009`009`009if ((!name_is_fill && named_macro) ||
X`009`009`009    (name_is_fill && !named_macro)) {
X`009`009`009`009break;
X`009`009`009}
X`009`009}
X`009`009prev_literal = !prev_literal && (*exp == NEXT_LITERAL);
X`009`009*fillptr++ = *exp++;
X`009}
X`009if (prev_literal && *exp != '\0') {
X`009`009exp--;
X`009`009fillptr--;
X`009}
X`009(*strptr) = exp;
X`009*fillptr = '\0';
X`009return (fillptr - fillbuf);
X}
X
X/* strptr points to a pointer to the first character of the name in a
Xnamed macro invocation.  This routine copies the macro name into nmbuf,
Xchecking the name length.  It sets the pointer to the name to the first
Xcharacter after the CLOSE_DELIM which terminates the macro name, or to
Xthe null which terminates the input string if no CLOSE_DELIM is found. 
XThe return value is -1 for error, 0 for success. */
X
Xstatic
Xget_macro_name(strptr, nmbuf, max_len)
Xchar **strptr;
Xchar *nmbuf;
Xint max_len;
X{
X`009register char *expstr, *nmptr;
X
X`009expstr = *strptr;
X`009nmptr = nmbuf;
X`009while (*expstr && *expstr != CLOSE_DELIM) {
X`009`009if (nmptr - nmbuf < max_len) {
X`009`009`009*nmptr++ = *expstr;
X`009`009}
X`009`009expstr++;
X`009}
X`009*nmptr = '\0';
X`009*strptr = expstr;
X`009if (*expstr != CLOSE_DELIM) {
X`009`009return (-1);`009/* return error */
X`009}
X`009(*strptr)++;
X`009return (0);`009/* return success */
X}
X
X/* strptr is address of pointer to current position in string to expand.
XAn INVOKE_MACRO character was just found; **strptr is the character after
Xit.  This routine reads the separator character (if any) and saves it.
XIt also skips past the OPEN_DELIM character.  It returns -1 on error, 0
Xif everything is okay, or 1 if the INVOKE_MACRO prefaced a special macro
Xinvocation.  (In this last case, the string pointer is set to the first
Xcharacter after the special macro character.)  Possible errors are
Xmissing OPEN_DELIM, unexpected end of input string, and separator
Xcharacter conflict. */
X
Xstatic
Xget_delim(strptr, sepptr, tokptr)
Xchar **strptr;
Xint *sepptr;
XMAKE_TOKEN *tokptr;
X{
X`009int c;
X
X#ifdef SPEC_EXP_DEBUG
Xprintf("[get_delim] Starting, string is '%s'\n", *strptr);
X#endif
X
X`009c = **strptr;
X`009(*strptr)++;
X`009switch (c) {
X
X`009/* NEXT_LITERAL means following character is separator; end of
X`009input string after NEXT_LITERAL causes fall-through to case '\0' */
X
X`009case NEXT_LITERAL:
X`009`009c = **strptr;
X`009`009(*strptr)++;
X`009`009if (c != '\0') {
X`009`009`009if (bad_sep(sepptr, c, tokptr)) {
X`009`009`009`009return (-1);
X`009`009`009}
X`009`009`009break;
X`009`009}
X
X`009/* End of input string; last character is INVOKE_MACRO. */
X
X`009case '\0':
X`009`009(*strptr)--;`009/* back up to point at null */
X#ifdef VMS_ERROR
X`009`009lib$signal(&Mak_nodelim, 7,
X`009`009    1, &OPEN_DELIM, 1, &INVOKE_MACRO,
X`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009printf("  ** Missing '%c' after '%c' in '%s', line %d.\n",
X`009`009    OPEN_DELIM, INVOKE_MACRO, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009return (-1);
X
X`009/* No separator character used; return okay. */
X
X`009case OPEN_DELIM:
X`009`009return (0);
X
X`009/* Special macro character after INVOKE_MACRO. */
X
X`009case CURRENT_TARGET:
X`009case CURR_TARG_NO_EXT:
X`009case DEPENDENCY_LIST:
X`009case OUT_OF_DATE_DEPS:
X`009`009(*strptr)--;`009/* back up one to point at special character */
X#ifdef SPEC_EXP_DEBUG
Xprintf("[get_delim] Found special macro:  '%s'\n", *strptr);
X#endif
X`009`009return (1);
X
X`009/* Any other character is an ordinary separator. */
X
X`009default:
X`009`009if (bad_sep(sepptr, c, tokptr)) {
X`009`009`009return (-1);
X`009`009}
X`009`009break;
X`009}
X
X`009/* Get delimiter or special macro character
X`009which follows separator character. */
X
X`009c = **strptr;
X`009(*strptr)++;
X`009if (IS_SPECIAL(c)) {
X`009`009(*strptr)--;
X`009`009return (1);
X`009}
X`009if (c != OPEN_DELIM) {
X#ifdef VMS_ERROR
X`009`009lib$signal(&Mak_chrequired, 5, 1, &OPEN_DELIM,
X`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009printf("  ** Missing '%c' in '%s', line %d.\n",
X`009`009    OPEN_DELIM, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009return (-1);
X`009}
X`009return (0);
X}
X
X/* Check for conflicting separator characters.  Return TRUE for
Xconflict.  Save new separator if no old separator given. */
X
Xstatic
Xbad_sep(oldptr, new, tokptr)
Xint *oldptr;
Xint new;
XMAKE_TOKEN *tokptr;
X{
X`009if (*oldptr != '\0' && *oldptr != new) {
X#ifdef VMS_ERROR
X`009`009lib$signal(&Mak_sepclash, 7, 1, oldptr, 1, &new,
X`009`009    FAO_AD(tokptr->mt_text), tokptr->mt_line);
X#else
X`009`009printf("  ** Conflicting separators '%c' and '%c' in '%s', line %d.\n",
X`009`009    *oldptr, new, tokptr->mt_text, tokptr->mt_line);
X#endif
X`009`009return (TRUE);
X`009}
X`009*oldptr = new;
X`009return (FALSE);
X}
X
X/* Combine the current items to create the latest instance of the macro
Xstring being expanded.  Allocate a copy of the string and return its
Xaddress, or NULL if the item list is used up. */
X
Xstatic
Xchar *
Xmacro_instance(item_list, n_item, separator)
XMACRO_ITEM *item_list;
Xint n_item, separator;
X{
X`009int i_len, last_instance;
X`009char *i_ptr, *text;
X`009char instance[MAX_EXP_INSTANCE + 2];
X
X`009i_ptr = instance;
X`009last_instance = FALSE;
X`009while (n_item-- > 0) {
X`009`009if (item_list->it_type == ITEM_TEXT) {
X`009`009`009text = item_list->it_text;
X`009`009} else {
X`009`009`009if (item_list->it_token == NULL) {
X`009`009`009`009return (NULL);
X`009`009`009}
X`009`009`009text = item_list->it_token->mt_text;
X`009`009`009item_list->it_token = item_list->it_token->mt_next;
X`009`009`009if (item_list->it_token == NULL) {
X`009`009`009`009last_instance = TRUE;
X`009`009`009}
X`009`009}
X`009`009item_list++;
X`009`009if (strlen(text) + (i_ptr - instance) >= sizeof(instance)) {
X`009`009`009*i_ptr = '\0';
X`009`009`009printf("FATAL ERROR, macro expansion too long:\n");
X`009`009`009printf("%s%s ...\n", instance, text);
X`009`009`009make_exit(NORMAL_EXIT);
X`009`009}
X`009`009i_ptr = strecpy(text, i_ptr);
X`009}
X
X`009/* Check for use of separator character. */
X
X`009if (separator != '\0') {
X`009`009text = err_alloc((i_ptr - instance) + 2);
X`009`009i_ptr = strecpy(instance, text);
X`009`009*i_ptr++ = separator;
X`009`009*i_ptr = '\0';
X`009`009return (text);
X`009}
X
X`009/* If no separator used, or this is the last instance of
X`009the macro expansion, return the instance without appending
X`009a separator. */
X
X`009return (newstr(instance, i_ptr - instance));
X}
$ GoSub Convert_File
$ Goto Part7