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

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

$Part5:
$ File_is="MKFILE.C"
$ Check_Sum_is=694782456
$ 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
Xmkfile.c
XThis file contains the routines which parse makefiles and create the
Xnamed macro, default rule, and target statements.
X*/
X
X#include "make.h"
X
X/* Read the named makefile, placing definitions of
Xtargets, macros, and rules in the lists provided. */
X
Xget_make_file(fname, mlptr, rlptr, tlptr)
Xchar *fname;
XMAKE_TARGET **mlptr, **rlptr, **tlptr;
X{
X`009MAKE_TOKEN *toklist;
X`009FILE *mkfptr;
X
X`009/* Open named makefile. */
X
X`009if (fname == NULL || *fname == '\0') {
X`009`009mkfptr = NULL;
X`009} else if ((mkfptr = fopen(fname, "r")) == NULL) {
X
X`009`009/* Try to add default makefile type. */
X
X
X
X`009`009if ((mkfptr = fopen(fname, "r")) == NULL) {
X`009`009`009if (Verbose) {
X`009`009`009`009print_prefix();
X`009`009`009`009printf("Can't open file '%s'\n", fname);
X`009`009`009}
X`009`009`009return (0);
X`009`009}
X`009}
X
X`009/* Read makefile in for processing. */
X
X`009toklist = make_token_list(mkfptr);
X`009if (mkfptr != NULL) {
X`009`009fclose(mkfptr);
X`009}
X
X`009/* Parse token list, appending results to
X`009rule list, macro list, and target list. */
X
X`009return (parse_file(toklist, mlptr, rlptr, tlptr));
X}
X
X/* Check the syntax of a list of tokens representing a makefile. */
X
Xparse_file(tokptr, mlptr, rlptr, tlptr)
Xregister MAKE_TOKEN *tokptr;
XMAKE_TARGET **mlptr, **rlptr, **tlptr;
X{
X`009register MAKE_TARGET *tgptr;
X`009register MAKE_COMMAND *cmdptr;
X`009int c, parse_state, parse_error, experr;
X`009char *errptr;
X`009MAKE_TOKEN *prevtok, *alttok;
X`009MAKE_COMMAND *prevcmd;
X
X#ifdef FILE_DEBUG
Xprintf("\n[parse_file] Starting, lists are:\n");
Xshow_target(*mlptr, STMT_MACRO);
Xshow_target(*rlptr, STMT_RULE);
Xshow_target(*tlptr, STMT_TARGET);
Xprintf(".................................\n");
X#endif
X
X`009/* Initialize to parse makefile. */
X
X`009tgptr = NULL;
X`009parse_error = FALSE;
X`009parse_state = P_START_LINE;
X
X`009/* Scan through entire token list. */
X
X`009while (tokptr != NULL || tgptr != NULL) {
X#ifdef PARSE_DEBUG
Xif (tokptr != NULL && tokptr->mt_text != NULL) {
Xprintf("[parse] State = %s, token '%s', line %d\n", P_state_name[parse_state],
X(*tokptr->mt_text == '\n' ? "\\n" : tokptr->mt_text), tokptr->mt_line);
X}
X#endif
X`009`009switch (parse_state) {
X
X`009`009/* This state is entered whenever a newline token is
X`009`009followed by anything but a space token.  On entry,
X`009`009tokptr points at the token after the newline. */
X
X`009`009case P_START_LINE:
X
X`009`009`009if (tgptr != NULL) {
X#ifdef PARSE_DEBUG
Xprintf("  ** Warning, tgptr not NULL on entering P_START_LINE.\n");
X#endif
X`009`009`009`009free_target(tgptr);
X`009`009`009}
X`009`009`009tgptr = NULL;
X
X`009`009`009/* Skip consecutive newlines. */
X
X`009`009`009while (tokptr != NULL && *tokptr->mt_text == '\n') {
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009}
X
X`009`009`009/* Check for tokens which are invalid
X`009`009`009at the start of a line. */
X
X`009`009`009while (tokptr != NULL && tokptr->mt_text[1] == '\0' &&
X`009`009`009    ((c = *tokptr->mt_text) == ' ' ||
X`009`009`009    c == DEPENDENCY || c == DEFINE_MACRO)) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_badinitch, 3,
X`009`009`009`009    1, &c, tokptr->mt_line);
X#else
V`009`009`009`009printf("  ** Bad initial character '%c' on line %d, deleted.\n"
X,
X`009`009`009`009    c, tokptr->mt_line);
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009}
X
X`009`009`009/* Change state to read list of targets.  Note that
X`009`009`009the current token is left in the input list to be
X`009`009`009processed with any following target tokens. */
X
X`009`009`009if (tokptr != NULL && *tokptr->mt_text != '\n') {
X`009`009`009`009parse_state = P_GET_TARGET_LIST;
X`009`009`009}
X`009`009`009break;
X
X`009`009/* This state reads any number of target tokens from the
X`009`009input list.  The target token list ends when a newline,
X`009`009DEPENDENCY, or DEFINE_MACRO token is found. */
X
X`009`009case P_GET_TARGET_LIST:
X
X`009`009`009/* Create a MAKE_TARGET to hold the target list. */
X
X`009`009`009tgptr = E_ALLOC(MAKE_TARGET);
X`009`009`009tgptr->tg_next = NULL;
X`009`009`009tgptr->tg_name = NULL;
X`009`009`009tgptr->tg_dep = NULL;
X`009`009`009tgptr->tg_cmd = NULL;
X`009`009`009tgptr->tg_ndep = 0;
X
X`009`009`009/* Read target tokens until reaching a newline,
X`009`009`009DEPENDENCY, or DEFINE_MACRO token. */
X
X`009`009`009prevtok = NULL;
X`009`009`009while (tokptr->mt_text[1] != '\0' ||
X`009`009`009    ((c = *tokptr->mt_text) != '\n' &&
X`009`009`009    c != DEPENDENCY && c != DEFINE_MACRO)) {
X`009`009`009`009if (tgptr->tg_name == NULL) {
X`009`009`009`009`009tgptr->tg_name = tokptr;
X`009`009`009`009}
X`009`009`009`009prevtok = tokptr;
X`009`009`009`009tokptr = tokptr->mt_next;
X`009`009`009`009if (tokptr == NULL) {
X#ifdef VMS_ERROR
X`009`009`009`009`009lib$signal(&Mak_nodepch, 5,
X`009`009`009`009`009    1, &DEPENDENCY,
X`009`009`009`009`009    FAO_AD(prevtok->mt_text),
X`009`009`009`009`009    prevtok->mt_line);
X`009`009`009`009`009lib$signal(&Mak_unexpeof);
X#else
X`009`009`009`009`009printf("  ** Expected '%c' after '%s', line %d.\n",
X`009`009`009`009`009    DEPENDENCY, prevtok->mt_text, prevtok->mt_line);
X`009`009`009`009`009printf("  ** End of file not preceded by newline.\n");
X#endif
X`009`009`009`009`009parse_error = TRUE;
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009}
X`009`009`009if (tokptr == NULL) {
X`009`009`009`009break;
X`009`009`009}
X`009`009`009if (prevtok != NULL) {
X`009`009`009`009prevtok->mt_next = NULL;
X`009`009`009}
X
X`009`009`009/* Decide what to do next based on what
X`009`009`009token ended target name list. */
X
X`009`009`009alttok = tokptr;
X`009`009`009tokptr = tokptr->mt_next;
X`009`009`009alttok->mt_next = NULL;
X`009`009`009switch (c = *alttok->mt_text) {
X`009`009`009case '\n':
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_depneeded, 3,
X`009`009`009`009    1, &DEPENDENCY, prevtok->mt_line);
X#else
V`009`009`009`009printf("  ** Missing '%c' on line %d; inserted at end of line.\
Xn",
X`009`009`009`009    DEPENDENCY, prevtok->mt_line);
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009`009parse_state = P_GET_CMD;
X`009`009`009`009break;
X`009`009`009case ' ':
X`009`009`009case DEPENDENCY:
X`009`009`009case DEFINE_MACRO:
X`009`009`009`009if (tokptr == NULL) {
X`009`009`009`009`009if (c == ' ') {
X#ifdef VMS_ERROR
X`009`009`009`009`009`009lib$signal(&Mak_invblank, 1,
X`009`009`009`009`009`009    alttok->mt_line);
X#else
V`009`009`009`009`009`009printf("  ** Blank invalid in target specification, lin
Xe %d.\n",
X`009`009`009`009`009`009    alttok->mt_line);
X#endif
X`009`009`009`009`009} else if (c == DEPENDENCY) {
X#ifdef VMS_ERROR
X`009`009`009`009`009`009lib$signal(&Mak_depeof, 3,
X`009`009`009`009`009`009    1, &DEPENDENCY,
X`009`009`009`009`009`009    alttok->mt_line);
X#else
V`009`009`009`009`009`009printf("  ** No dependencies or commands after '%c' on 
Xline %d.\n",
X`009`009`009`009`009`009    DEPENDENCY, alttok->mt_line);
X#endif
X`009`009`009`009`009} else {
X#ifdef VMS_ERROR
X`009`009`009`009`009`009lib$signal(&Mak_notextmac, 3,
X`009`009`009`009`009`009    FAO_AD(prevtok->mt_text),
X`009`009`009`009`009`009    prevtok->mt_line);
X#else
X`009`009`009`009`009`009printf("  ** No text for macro '%s', line %d\n",
X`009`009`009`009`009`009    prevtok->mt_text, prevtok->mt_line);
X#endif
X`009`009`009`009`009}
X`009`009`009`009`009parse_error = TRUE;
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009`009if (c == DEPENDENCY) {
X`009`009`009`009`009parse_state = P_GET_DEPENDENCY_LIST;
X`009`009`009`009} else {
X`009`009`009`009`009parse_state = P_GET_MACRO_TEXT;
X`009`009`009`009}
X`009`009`009`009break;
X`009`009`009default:
V`009`009`009`009printf("FATAL ERROR, line %d: target name list terminated at '%
Xs'\n",
X`009`009`009`009    alttok->mt_line, alttok->mt_text);
X`009`009`009`009make_exit(NORMAL_EXIT);
X`009`009`009}
X`009`009`009free_token(alttok);
X`009`009`009break;
X
X`009`009/* This state is reached after reading the DEPENDENCY token
X`009`009in a target definition.  It reads all following names, up
X`009`009to a newline, into the dependency list for the target being
X`009`009created.  DEPENDENCY and DEFINE_MACRO tokens are not allowed
X`009`009in the dependency list.  On entry, tokptr is set to the
X`009`009first token after the DEPENDENCY in the input list, and
X`009`009tgptr points to a MAKE_TARGET which has been given the
X`009`009list of targets the dependencies apply to. */
X
X`009`009case P_GET_MACRO_TEXT:
X`009`009case P_GET_DEPENDENCY_LIST:
X`009`009`009if (tgptr == NULL) {
X`009`009`009`009printf("FATAL ERROR, tgptr is NULL in P_GET_%s\n",
X`009`009`009`009    parse_state == P_GET_DEPENDENCY_LIST ?
X`009`009`009`009    "DEPENDENCY_LIST" : "MACRO_TEXT");
X`009`009`009`009make_exit(NORMAL_EXIT);
X`009`009`009}
X`009`009`009prevtok = NULL;
X`009`009`009while (tokptr != NULL && (c = *tokptr->mt_text) != '\n') {
X`009`009`009`009if (tokptr->mt_text[1] == '\0' &&
X`009`009`009`009    (c == DEPENDENCY || c == DEFINE_MACRO)) {
X`009`009`009`009`009errptr = "dependency list";
X`009`009`009`009`009if (parse_state == P_GET_MACRO_TEXT) {
X`009`009`009`009`009`009errptr = "macro text";
X`009`009`009`009`009}
X#ifdef VMS_ERROR
X`009`009`009`009`009lib$signal(&Mak_invch, 5, 1, &c,
X`009`009`009`009`009    FAO_AD(errptr), tokptr->mt_line);
X#else
X`009`009`009`009`009printf("  ** '%c' invalid in %s, on line %d; deleted.\n",
X`009`009`009`009`009    c, errptr, tokptr->mt_line);
X#endif
X`009`009`009`009`009parse_error = TRUE;
X`009`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009`009`009if (prevtok != NULL) {
X`009`009`009`009`009`009prevtok->mt_next = tokptr;
X`009`009`009`009`009}
X`009`009`009`009} else {
X`009`009`009`009`009if (tgptr->tg_dep == NULL) {
X`009`009`009`009`009`009tgptr->tg_dep = tokptr;
X`009`009`009`009`009}
X`009`009`009`009`009prevtok = tokptr;
X`009`009`009`009`009tokptr = tokptr->mt_next;
X`009`009`009`009}
X`009`009`009}
X
X`009`009`009if (tokptr == NULL) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_unexpeof);
X#else
X`009`009`009`009printf("  ** End of file not preceded by newline.\n");
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009break;
X`009`009`009}
X`009`009`009if (prevtok != NULL) {
X`009`009`009`009prevtok->mt_next = NULL;
X`009`009`009}
X`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009if (parse_state == P_GET_MACRO_TEXT) {
X`009`009`009`009parse_state = P_CHECK_MACRO;
X`009`009`009} else {
X`009`009`009`009parse_state = P_GET_CMD;
X`009`009`009}
X`009`009`009break;
X
X`009`009case P_CHECK_MACRO:
X`009`009`009experr = macro_expand(tgptr->tg_name, *mlptr);
X`009`009`009if (tgptr->tg_name->mt_next != NULL) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_multmacdef, 1,
X`009`009`009`009    tgptr->tg_name->mt_line);
X#else
V`009`009`009`009printf("  ** More than one name in macro definition, line %d.\n
X",
X`009`009`009`009    tgptr->tg_name->mt_line);
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009} else if (experr != EXP_SIMPLE) {
X`009`009`009`009if (experr == EXP_SPECIAL) {
X#ifdef VMS_ERROR
X`009`009`009`009`009lib$signal(&Mak_specdef, 1,
X`009`009`009`009`009    tgptr->tg_name->mt_line);
X#else
X`009`009`009`009`009printf("  ** Redefinition of special macro, line %d.\n",
X`009`009`009`009`009    tgptr->tg_name->mt_line);
X#endif
X`009`009`009`009}
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009} else if (macro_expand(tgptr->tg_dep, *mlptr) ==
X`009`009`009    EXP_ERROR || !macro_add(tgptr, mlptr)) {
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009}
X`009`009`009tgptr = NULL;
X`009`009`009parse_state = P_START_LINE;
X`009`009`009break;
X
X`009`009/* This state is reached after reading a dependency line and
X`009`009any associated command lines.  It expands any named macros in
X`009`009the dependency or command lines and error checks the result. */
X
X`009`009case P_EXPAND_TARGET:
X`009`009`009if (tgptr == NULL) {
X#ifdef PARSE_DEBUG
X`009`009`009`009printf("FATAL ERROR:  tgptr is NULL on entering %s.\n",
X`009`009`009`009    P_state_name[parse_state]);
X#endif
X`009`009`009`009make_exit(NORMAL_EXIT);
X`009`009`009}
X`009`009`009experr = macro_expand(tgptr->tg_name, *mlptr);
X`009`009`009if (experr == EXP_SPECIAL) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_spectarg, 3, FAO_AD("name"),
X`009`009`009`009    tgptr->tg_name->mt_line);
X#else
X`009`009`009`009printf("  ** Special macro used in target name, line %d.\n",
X`009`009`009`009    tgptr->tg_name->mt_line);
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009} else if (tgptr->tg_name == NULL) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_notarg, 1,
X`009`009`009`009    tgptr->tg_name->mt_line);
X#else
X`009`009`009`009printf("  ** No target name given, line %d.\n",
X`009`009`009`009    tgptr->tg_name->mt_line);
X#endif
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009} else if (experr == EXP_ERROR ||
X`009`009`009    macro_expand(tgptr->tg_dep, *mlptr) == EXP_ERROR ||
X`009`009`009    cmd_expand(tgptr->tg_cmd, *mlptr) == EXP_ERROR) {
X`009`009`009`009parse_error = TRUE;
X`009`009`009`009free_target(tgptr);
X`009`009`009} else if (save_target(tgptr, rlptr, tlptr) < 0) {
X`009`009`009`009parse_error = TRUE;
X`009`009`009}
X`009`009`009tgptr = NULL;
X`009`009`009parse_state = P_START_LINE;
X`009`009`009break;
X
X`009`009/* This state is reached after reading a dependency line.
X`009`009It checks to see if the next line is a command line which
X`009`009tells how to update the target.  If the next line is a
X`009`009command line, it changes state to read the line, otherwise
X`009`009the current target is completely read, so it changes state
X`009`009to expand and save it. */
X
X`009`009case P_GET_CMD:
X`009`009`009if (tokptr != NULL && tokptr->mt_text[1] == '\0' &&
X`009`009`009    *tokptr->mt_text == BEGIN_CMD_LINE) {
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009`009if (tgptr->tg_cmd == NULL) {
X`009`009`009`009`009prevcmd = NULL;
X`009`009`009`009}
X`009`009`009`009parse_state = P_GET_CMD_FLAG;
X`009`009`009} else {
X`009`009`009`009parse_state = P_EXPAND_TARGET;
X`009`009`009}
X`009`009`009break;
X
X`009`009/* This state is reached after finding a BEGIN_CMD_LINE after
X`009`009a newline.  It reads the rest of the command line. */
X
X`009`009case P_GET_CMD_FLAG:
X`009`009`009cmdptr = E_ALLOC(MAKE_COMMAND);
X`009`009`009cmdptr->cmd_next = cmdptr->cmd_word = NULL;
X`009`009`009cmdptr->cmd_flag = 0;
X`009`009`009while (tokptr->mt_text[1] == '\0') {
X`009`009`009`009c = *tokptr->mt_text;
X`009`009`009`009if (c == MC_IGN_ERROR) {
X`009`009`009`009`009cmdptr->cmd_flag |= CMD_IGN_ERROR;
X`009`009`009`009} else if (c == MC_NO_ECHO) {
X`009`009`009`009`009cmdptr->cmd_flag |= CMD_NO_ECHO;
X`009`009`009`009} else if (c == '\n') {
X#ifdef VMS_ERROR
X`009`009`009`009`009lib$signal(&Mak_nocmd, 1,
X`009`009`009`009`009    tokptr->mt_line);
X#else
X`009`009`009`009`009printf("  ** No command text on line %d.\n",
X`009`009`009`009`009    tokptr->mt_line);
X#endif
X/* this could be made a warning only */`009parse_error = TRUE;
X`009`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009`009`009parse_state = P_GET_CMD;
X`009`009`009`009`009free(cmdptr);
X`009`009`009`009`009cmdptr = NULL;
X`009`009`009`009`009break;
X`009`009`009`009} else {
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009}
X
X`009`009`009/* Check for error during flag processing. */
X
X`009`009`009if (cmdptr == NULL) {
X`009`009`009`009break;
X`009`009`009}
X
X`009`009`009/* Get words in command.  First token is not newline. */
X
X`009`009`009prevtok = NULL;
X`009`009`009cmdptr->cmd_word = tokptr;
X`009`009`009while (tokptr != NULL && *tokptr->mt_text != '\n') {
X`009`009`009`009prevtok = tokptr;
X`009`009`009`009tokptr = tokptr->mt_next;
X`009`009`009}
X`009`009`009prevtok->mt_next = NULL;
X`009`009`009if (tokptr != NULL) {
X`009`009`009`009tokptr = free_first_token(tokptr);
X`009`009`009}
X
X`009`009`009/* Append latest command to current MAKE_TARGET. */
X
X`009`009`009if (prevcmd == NULL) {
X`009`009`009`009tgptr->tg_cmd = cmdptr;
X`009`009`009} else {
X`009`009`009`009prevcmd->cmd_next = cmdptr;
X`009`009`009}
X`009`009`009prevcmd = cmdptr;
X`009`009`009cmdptr = NULL;
X
X`009`009`009/* Change state to check for another command line. */
X
X`009`009`009parse_state = P_GET_CMD;
X`009`009`009break;
X
X`009`009default:
X`009`009`009printf("FATAL ERROR, line %d: bad parser state %d.\n",
X`009`009`009    tokptr->mt_line, parse_state);
X`009`009`009make_exit(NORMAL_EXIT);
X`009`009}
X`009}
X
X`009/* See if any errors were found in the token list. */
X
X`009if (parse_error) {
X`009`009return (-1);
X`009}
X`009return (0);
X}
X
X/* The target pointed to by tgptr contains no named macros (they have all
Xbeen previously expanded.  It may contain special macros.  The target name
Xlist may contain more than one file name, and/or more than one rule
Xdefinition.  This routine splits these multiple target definitions into
Xsingle target/rule definitions and merges them with any previously created
Xtarget/rule definitions. */
X
Xstatic
Xsave_target(tgptr, rlptr, tlptr)
XMAKE_TARGET *tgptr;
XMAKE_TARGET **rlptr, **tlptr;
X{
X`009register MAKE_TOKEN *nmptr, *nextptr;
X`009int target_type;
X`009MAKE_COMMAND *cmd;
X`009MAKE_TARGET *newtg;
X
X`009/* Create a new target for each valid name in the target name list. */
X
X`009for (nmptr = tgptr->tg_name; nmptr != NULL; nmptr = nextptr) {
X
X`009`009/* Detach first target name token from list of names. */
X
X`009`009nextptr = nmptr->mt_next;
X`009`009nmptr->mt_next = NULL;
X
X`009`009/* Check for errors in target name. */
X
X`009`009target_type = check_tname(nmptr->mt_text, nmptr->mt_line);
X
X`009`009/* If error found in name, don't save. */
X
X`009`009if (target_type < STMT_ERROR) {
X`009`009`009return (-1);
X`009`009} 
X
X`009`009/* Since name is valid, perform final clean-up on it. */
X
X`009`009char_process(nmptr, FALSE);
X
X`009`009/* Create new MAKE_TARGET. */
X
X`009`009newtg = E_ALLOC(MAKE_TARGET);
X`009`009newtg->tg_name = nmptr;
X`009`009newtg->tg_cmd = NULL;
X`009`009newtg->tg_next = NULL;
X
X`009`009/* Copy dependency list and expand special macros
X`009`009in it using the current target name. */
X
X`009`009new_copy_token(tgptr->tg_dep, &newtg->tg_dep, &newtg->tg_ndep);
X`009`009if (expand_special(newtg->tg_dep,
X`009`009    newtg->tg_name, NULL) == EXP_ERROR) {
X`009`009`009return (-1);
X`009`009}
X`009`009char_process(newtg->tg_dep, FALSE);
X
X`009`009/* Copy update commands and expand special macros in them 
X`009`009using the current target name and dependency list. */
X
X`009`009new_copy_cmd(tgptr->tg_cmd, &newtg->tg_cmd);
X
X`009`009/* Save target or rule in appropriate list. */
X
X`009`009if (target_type == STMT_TARGET) {
X`009`009`009if (cmd_exp_special(newtg->tg_cmd,
X`009`009`009    newtg->tg_name, newtg->tg_dep) != EXP_ERROR) {
X`009`009`009`009add_target(newtg, tlptr, TRUE);
X`009`009`009} else {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_cmdspecexp, 3,
X`009`009`009`009    FAO_AD(newtg->tg_name->mt_text),
X`009`009`009`009    newtg->tg_name->mt_line);
X#else
V`009`009`009`009printf("  ** Error expanding special macros in command line of 
Xtarget %s, line %d\n",
X`009`009`009`009    newtg->tg_name->mt_text, newtg->tg_name->mt_line);
X#endif
X`009`009`009`009return (-1);
X`009`009`009}
X`009`009} else {
X`009`009`009add_target(newtg, rlptr, FALSE);
X`009`009}
X`009}
X`009return (0);`009/* success */
X}
X
X/* Scan the name of a target.  Return STMT_ERROR if it is invalid (contains
Xa macro), STMT_TARGET if it is an ordinary target name, or STMT_RULE if it
Xis a default rule target (.xxx.yyy). */
X
Xstatic
Xcheck_tname(tname, line)
Xchar *tname;
Xint line;
X{
X`009register char *namestr;
X`009int prev_literal, first_ch_rule, is_rule;
X
X`009/* Make sure target name has at least one character. */
X
X`009if (tname == NULL || *tname == '\0') {
X#ifdef VMS_ERROR
X`009`009lib$signal(&Mak_notgname, 1, line);
X#else
X`009`009printf("  ** No name in target, line %d.\n", line);
X#endif
X`009`009return (STMT_ERROR);
X`009}
X
X`009/* Scan the name. */
X
X`009prev_literal = FALSE;
X`009namestr = tname;
X`009is_rule = FALSE;
X`009first_ch_rule = (*namestr == FILE_TYPE_CHAR);
X`009if (first_ch_rule) {
X`009`009namestr++;
X`009}
X`009while (*namestr) {
X`009`009switch (*namestr) {
X`009`009case INVOKE_MACRO:
X`009`009`009if (prev_literal) {
X`009`009`009`009break;
X`009`009`009}
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_spectarg, 3, FAO_AD(tname), line);
X#else
X`009`009`009printf("  ** Special macro in target '%s', line %d.\n",
X`009`009`009    tname, line);
X#endif
X`009`009`009return (STMT_ERROR);
X`009`009`009break;
X`009`009case FILE_TYPE_CHAR:
X`009`009`009if (prev_literal) {
X`009`009`009`009break;
X`009`009`009}
X`009`009`009if (first_ch_rule) {
X`009`009`009`009is_rule = TRUE;
X`009`009`009}
X`009`009`009break;
X`009`009}
X`009`009prev_literal = !prev_literal && (*namestr++ == NEXT_LITERAL);
X`009}
X`009return (is_rule ? STMT_RULE : STMT_TARGET);
X}
X
X/* Copy the list of commands pointed to by srctrg.  Allocate space for the
Xcopy and set the command pointer pointed to by dsttrgptr to point at the
Xnew list. */
X
Xnew_copy_target(srctrg, dsttrgptr)
XMAKE_TARGET *srctrg;
XMAKE_TARGET **dsttrgptr;
X{
X`009register MAKE_TARGET *dsttrg;
X
X`009*dsttrgptr = NULL;
X`009while (srctrg != NULL) {
X`009`009dsttrg = *dsttrgptr = E_ALLOC(MAKE_TARGET);
X`009`009dsttrg->tg_next = NULL;
X`009`009new_copy_token(srctrg->tg_name, &dsttrg->tg_name, NULL);
X`009`009new_copy_token(srctrg->tg_dep, &dsttrg->tg_dep,
X`009`009    &dsttrg->tg_ndep);
X`009`009new_copy_cmd(srctrg->tg_cmd, &dsttrg->tg_cmd);
X`009`009dsttrgptr = &dsttrg->tg_next;
X`009`009srctrg = srctrg->tg_next;
X`009}
X}
X
X/* Copy the list of commands pointed to by srccmd.  Allocate space for the
Xcopy and set the command pointer pointed to by dstcmdptr to point at the
Xnew list. */
X
Xnew_copy_cmd(srccmd, dstcmdptr)
XMAKE_COMMAND *srccmd;
XMAKE_COMMAND **dstcmdptr;
X{
X`009register MAKE_COMMAND *dstcmd;
X
X`009*dstcmdptr = NULL;
X`009while (srccmd != NULL) {
X`009`009dstcmd = *dstcmdptr = E_ALLOC(MAKE_COMMAND);
X`009`009dstcmd->cmd_next = NULL;
X`009`009dstcmd->cmd_flag = srccmd->cmd_flag;
X`009`009new_copy_token(srccmd->cmd_word, &dstcmd->cmd_word,
X`009`009    &dstcmd->cmd_length);
X`009`009dstcmdptr = &dstcmd->cmd_next;
X`009`009srccmd = srccmd->cmd_next;
X`009}
X}
X
X/* Copy the list of tokens pointed to by srctk.  Allocate space for the
Xcopy and set the token pointer pointed to by dsttkptr to point at the
Xnew list. */
X
Xnew_copy_token(srctk, dsttkptr, lenptr)
XMAKE_TOKEN *srctk;
XMAKE_TOKEN **dsttkptr;
XLISTLEN *lenptr;
X{
X`009register MAKE_TOKEN *dsttk;
X
X`009*dsttkptr = NULL;
X`009if (lenptr != NULL) {
X`009`009*lenptr = 0;
X`009}
X`009while (srctk != NULL) {
X`009`009dsttk = *dsttkptr = E_ALLOC(MAKE_TOKEN);
X`009`009dsttk->mt_next = NULL;
X`009`009dsttk->mt_text = newstr(srctk->mt_text, -1);
X`009`009dsttk->mt_simple = srctk->mt_simple;
X`009`009dsttk->mt_line = srctk->mt_line;
X`009`009if (lenptr != NULL) {
X`009`009`009*(lenptr)++;
X`009`009}
X`009`009dsttkptr = &dsttk->mt_next;
X`009`009srctk = srctk->mt_next;
X`009}
X}
X
X/* Append a new target to the target list.  If the target is already in
Xthe list, don't add the new one, but merge the dependency lists if the
Xmerge argument is non-zero. */
X
Xstatic
Xadd_target(ntptr, listptr, merge)
XMAKE_TARGET *ntptr;
XMAKE_TARGET **listptr;
Xint merge;
X{
X`009register MAKE_COMMAND *cmd;
X`009register MAKE_TARGET *otptr, *prevtptr;
X
X`009/* Perform final processing on command line text. */
X
X`009for (cmd = ntptr->tg_cmd; cmd != NULL; cmd = cmd->cmd_next) {
X`009`009cmd->cmd_flag |= char_process(cmd->cmd_word, TRUE);
X`009}
X
X`009/* Scan list to see if new target was defined previously. */
X
X`009prevtptr = NULL;
X`009for (otptr = *listptr; otptr != NULL; otptr = otptr->tg_next) {
X`009`009if (COMPARE(ntptr->tg_name->mt_text,
X`009`009    otptr->tg_name->mt_text) == 0) {
X`009`009`009if (otptr->tg_cmd == NULL) {
X`009`009`009`009otptr->tg_cmd = ntptr->tg_cmd;
X`009`009`009}
X`009`009`009if (merge) {
X`009`009`009`009merge_dep(ntptr->tg_dep, &otptr->tg_dep,
X`009`009`009`009    &otptr->tg_ndep);
X`009`009`009}
X`009`009`009return;
X`009`009}
X`009`009prevtptr = otptr;
X`009}
X
X`009/* Add new target to list. */
X
X`009if (prevtptr == NULL) {
X`009`009ntptr->tg_next = *listptr;
X`009`009*listptr = ntptr;
X`009} else {
X`009`009ntptr->tg_next = NULL;
X`009`009prevtptr->tg_next = ntptr;
X`009}
X}
X
X/* Merge the list of tokens pointed to by new into the list whose address is
Xpointed to by old.  Set the length of the resulting list in the value pointed
Xto by lenptr. */
X
Xmerge_dep(new, old, lenptr)
XMAKE_TOKEN *new;
XMAKE_TOKEN **old;
XLISTLEN *lenptr;
X{
X`009MAKE_TOKEN *ocurr, *oprev;
X
X`009while (new != NULL) {
X`009`009oprev = NULL;
X`009`009ocurr = *old;
X`009`009while (ocurr != NULL && COMPARE(ocurr->mt_text, new->mt_text)) {
X`009`009`009oprev = ocurr;
X`009`009`009ocurr = ocurr->mt_next;
X`009`009}
X`009`009if (ocurr == NULL) {
X`009`009`009ocurr = new->mt_next;
X`009`009`009if (oprev == NULL) {
X`009`009`009`009new->mt_next = *old;
X`009`009`009`009*old = new;
X`009`009`009} else {
X`009`009`009`009new->mt_next = oprev->mt_next;
X`009`009`009`009oprev->mt_next = new;
X`009`009`009}
X`009`009`009if (lenptr != NULL) {
X`009`009`009`009(*lenptr)++;
X`009`009`009}
X`009`009`009new = ocurr;
X`009`009} else {
X`009`009`009new = new->mt_next;
X`009`009}
X`009}
X}
X
X/* Delete all instances of single NEXT_LITERALs.  Convert pairs
Xof NEXT_LITERALs into single NEXT_LITERALs.  If a command line
Xis passed, scan for leading command control characters, and for
Xa trailing '-' (the DCL line continuation character).  Return
Xthe flags for this command line. */
X
Xchar_process(tokptr, is_cmd)
Xregister MAKE_TOKEN *tokptr;
Xint is_cmd;
X{
X`009register int cmdflag, startline;
X`009register char *str, *copy;
X
X`009cmdflag = 0;
X`009startline = TRUE;
X`009for (; tokptr != NULL; tokptr = tokptr->mt_next) {
X`009`009copy = str = tokptr->mt_text;
X`009`009while (*str) {
X`009`009`009if (startline) {
X`009`009`009`009if (*str == MC_IGN_ERROR) {
X`009`009`009`009`009cmdflag |= CMD_IGN_ERROR;
X`009`009`009`009`009str++;
X`009`009`009`009`009continue;
X`009`009`009`009} else if (*str == MC_NO_ECHO) {
X`009`009`009`009`009cmdflag |= CMD_NO_ECHO;
X`009`009`009`009`009str++;
X`009`009`009`009`009continue;
X`009`009`009`009} else {
X`009`009`009`009`009startline = FALSE;
X`009`009`009`009}
X`009`009`009}
X`009`009`009if (*str == NEXT_LITERAL) {
X`009`009`009`009if (*++str == '\0') {
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009}
X`009`009`009*copy++ = *str++;
X`009`009}
X`009`009*copy = '\0';
X`009}
X`009if (is_cmd && *--copy == MC_CONTINUE) {
X`009`009cmdflag |= CMD_CONTINUE;
X`009}
X`009return (cmdflag);
X}
$ GoSub Convert_File
$ Goto Part6