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

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

$Part7:
$ File_is="MKMAIN.C"
$ Check_Sum_is=1293804698
$ 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
Xmkmain.c
XThis file contains the main program, and the code which decides what
Xtargets to update and sends commands to the subprocess to do the
Xupdates.
X*/
X
X#include "make.h"
X
X/* Argument variables are not declared because this program
Xuses the CLI routines to get its command line parameters. */
X
Xmain()
X{
X`009int targlen, fdret;
X`009MAKE_TARGET *mlist, *rlist, *tlist;
X`009DATE datebuf;
X`009char datestr[ASCII_DATE_LEN];
X`009char drname[FILE_NAME_LEN + 1], fname[FILE_NAME_LEN + 1];
X
X`009/* First thing to do is establish exit handler, so that if an
X`009unexpected exit occurs, the sub-process is not left alive. */
X
X`009estexhand();
X
X`009/* Get simple qualifiers from command line. */
X
X`009Active = cli_present("ACTIVE") >= 0;
X`009Created = cli_present("REVISED") <= 0;
X`009Ignore = cli_present("IGNORE") > 0;
X`009Silent = cli_present("SILENT") > 0;
X`009Touch = cli_present("TOUCH") > 0;
X`009Verbose = cli_present("VERBOSE") > 0;
X
X`009/* See if a command file must be created. */
X
X`009fname[0] = '\0';
X`009Bldfile = NULL;
X`009if (cli_present("BUILD") > 0) {
X`009`009if (cli_get_value("BUILD", fname, sizeof(fname)) <= 0) {
X`009`009`009strecpy(Def_build_name, fname);
X`009`009}
X`009`009if (find_file_type(fname) == NULL) {
X`009`009`009set_file_type(fname, Def_build_type, fname);
X`009`009}
X`009`009Bldfile = fopen(fname, "w", "MRS=255", "RFM=VAR", "RAT=CR");
X`009`009if (Bldfile == NULL) {
X#ifdef VMS_ERROR
X`009`009`009lib$stop(&Mak_errcrebld, 2, FAO_AD(fname));
X#else
X`009`009`009printf("  ** Can't create command file %s\n", fname);
X`009`009`009make_exit(NORMAL_EXIT);
X#endif
X`009`009}
X`009}
X
X`009/* Set up to read makefiles. */
X
X`009init_ctype();
X`009mlist = rlist = tlist = NULL;
X
X`009/* Get possible command line macro definitions. */
X
X`009if (get_make_file(NULL, &mlist, &rlist, &tlist) < 0) {
X#ifdef VMS_ERROR
X`009`009lib$stop(&Mak_cmdlnerr);
X#else
X`009`009printf("  ** Errors in command line definitions.\n");
X#endif
X`009}
X
X`009/* See if special makefile name was given on command line. */
X
X`009fname[0] = '\0';
X`009if (cli_present("MAKEFILE") >= 0 &&
X`009    cli_get_value("MAKEFILE", fname, sizeof(fname)) <= 0) {
X`009`009strecpy(Def_makefile_name, fname);
X`009}
X`009translate(fname, fname, sizeof(fname) - 1);
X`009if (fname[0] && (*find_file_type(fname) == '\0')) {
X`009`009set_file_type(fname, Def_file_type, fname);
X`009}
X
X`009/* Read user makefile. */
X
X`009if (fname[0] != '\0' &&
X`009    get_make_file(fname, &mlist, &rlist, &tlist) < 0) {
X#ifdef VMS_ERROR
X`009`009lib$stop(&Mak_errinmak, 2, FAO_AD(fname));
X#else
X`009`009printf("  ** Errors in makefile %s, aborting.\n", fname);
X`009`009make_exit(NORMAL_EXIT);
X#endif
X`009}
X
X`009/* Check for special default rules definition file.  (This file
X`009defines the rules MAKE uses when the user makefile does not have
X`009specific instructions on how to make a given target.) */
X
X`009drname[0] = '\0';
X`009if (cli_present("RULE") >= 0 &&
X`009    cli_get_value("RULE", drname, sizeof(drname)) <= 0) {
X`009`009strecpy(Def_rule_name, drname);
X`009}
X`009translate(drname, drname, sizeof(drname) - 1);
X`009if (drname[0] && (*find_file_type(drname) == '\0')) {
X`009`009set_file_type(drname, Def_file_type, drname);
X`009}
X
X`009/* Read default rules definition file. */
X
X`009if (drname[0] != '\0' &&
X`009    get_make_file(drname, &mlist, &rlist, &tlist) < 0) {
X#ifdef VMS_ERROR
X`009`009lib$stop(&Mak_errindef, 2, FAO_AD(drname));
X#else
X`009`009printf("  ** Errors in default rule file %s, aborting.\n",
X`009`009    drname);
X`009`009make_exit(NORMAL_EXIT);
X#endif
X`009}
X
X`009/* If /PRINT option given, show all macros, rules, and dependencies. */
X
X`009if (cli_present("PRINT") > 0) {
X`009`009printf("#----------------------\n");
X`009`009show_target(mlist, STMT_MACRO);
X`009`009show_target(rlist, STMT_RULE);
X`009`009show_target(tlist, STMT_TARGET);
X`009`009printf("#----------------------\n");
X`009}
X
X`009/* Get name of first target to make. */
X
X`009if (cli_get_value("TARGET", fname, sizeof(fname) - 1) <= 0) {
X`009`009if (tlist == NULL) {
X#ifdef VMS_ERROR
X`009`009`009lib$stop(&Mak_nothing);
X#else
X`009`009`009printf("  ** Nothing to make.\n");
X`009`009`009make_exit(NORMAL_EXIT);
X#endif
X`009`009}
X`009`009strecpy(tlist->tg_name->mt_text, fname);
X`009}
X
X`009/* Update the first target, and any others from command line. */
X
X`009do {
X`009`009Update_level = 0;
X`009`009if (Bldfile != NULL) {
X`009`009`009fprintf(Bldfile, "$\n$! Creating %s\n$\n", fname);
X`009`009}
X`009`009update(fname, rlist, tlist);
X`009} while (cli_get_value("TARGET", fname, sizeof(fname) - 1) > 0);
X
X`009/* Clean-up and exit. */
X
X`009make_exit(NORMAL_EXIT);
X}
X
X/* Make sure named target is up to date with respect to its dependencies.
XReturn UPDT_OKAY for success, UPDT_ERROR for error, or UPDT_HOW if don't
Xknow how to update. */
X
Xstatic
Xupdate(targetname, rlist, tlist)
Xchar *targetname;
XMAKE_TARGET *rlist, *tlist;
X{
X`009MAKE_TARGET *find_target(), *usable_rule();
X`009int dep_exists, remake_target;
X`009char *deptype, *targtype;
X`009MAKE_TOKEN *dptr, *ruledep;
X`009MAKE_TARGET *rlptr, *tptr, *saveptr, *newptr;
X`009DATE depdate, targdate;
X`009char rlsrcname[FILE_NAME_LEN + 1], updtname[FILE_NAME_LEN + 1];
X`009char ruletarg[FILE_NAME_LEN + 1];
X
X`009/* Kludge to ignore use of empty macros.  This should properly
X`009be taken care of by the code which expands the macros. */
X
X`009if (targetname == NULL || *targetname == '\0') {
X`009`009return (UPDT_OKAY);
X`009}
X
X`009/* See if any way of updating the target is known. */
X
X`009tptr = find_target(targetname, tlist);
X`009if (tptr == NULL && ((targtype = find_file_type(targetname)) == NULL ||
X`009    *targtype == '\0')) {
X`009`009targetname = set_file_type(targetname, Def_targ_type, NULL);
X`009`009tptr = find_target(targetname, tlist);
X`009}
X
X`009/* Get the date of the target file.  If an error occurs, this is
X`009assumed to mean the target does not exist (and therefore is "out
X`009of date", since it needs to be created). */
X
X`009remake_target = (get_file_date(targetname, &targdate) < 0);
X`009verbose_show(targetname, !remake_target, &targdate);
X
X`009/* If no way of making the target is given in the makefile,
X`009try to find a default rule which applies. */
X
X`009rlptr = NULL;
X`009if (tptr == NULL) {
X
X`009`009/* See if a default rule can be used. */
X
X`009`009rlptr = usable_rule(rlist, targetname, rlsrcname, &depdate);
X`009`009if (tptr == NULL && rlptr == NULL) {
X`009`009`009if (remake_target) {
X#ifdef VMS_ERROR
X`009`009`009`009lib$signal(&Mak_how, 2, FAO_AD(targetname));
X#else
X`009`009`009`009printf("  ** Don't know how to make %s\n",
X`009`009`009`009    targetname);
X#endif
X`009`009`009`009return (UPDT_HOW);
X`009`009`009}
X`009`009`009return (UPDT_OKAY);
X`009`009}
X`009} else if (tptr->tg_cmd == NULL) {
X
X`009`009/* The current target file is described in the makefile,
X`009`009but the dependency statement describing it has no DCL
X`009`009command lines.  Therefore a default rule must be found
X`009`009which tells how to update the target file. */
X
X`009`009targtype = find_file_type(tptr->tg_name->mt_text);
X`009`009if (targtype != NULL) {
X`009`009`009dptr = tptr->tg_dep;
X`009`009`009while (dptr != NULL) {
X`009`009`009`009deptype = find_file_type(dptr->mt_text);
X`009`009`009`009if (deptype != NULL) {
X`009`009`009`009`009strecpy(targtype, strecpy(deptype,
X`009`009`009`009`009    ruletarg));
X`009`009`009`009`009rlptr = find_target(ruletarg, rlist);
X`009`009`009`009`009if (rlptr != NULL) {
X`009`009`009`009`009`009strecpy(dptr->mt_text, rlsrcname);
X`009`009`009`009`009`009break;
X`009`009`009`009`009}
X`009`009`009`009}
X`009`009`009`009dptr = dptr->mt_next;
X`009`009`009}
X`009`009}
X`009`009if (rlptr == NULL) {
X`009`009`009rlptr = usable_rule(rlist, targetname,
X`009`009`009    rlsrcname, &depdate);
X`009`009}
X`009}
X
X`009/* If a default rule is being used, generate a dependency from it. */
X
X`009if (rlptr != NULL) {
X
X`009`009/* At this point, rlptr points to the default rule to use
X`009`009to create the current target.  The modification date (either
X`009`009creation or revision) of the source file for the rule is in
X`009`009depdate.  Create a MAKE_TARGET which shows how the rule is
X`009`009used in this particular case. */
X
X`009`009if (Verbose) {
X`009`009`009print_prefix();
X`009`009`009printf("Using default rule %s for target %s\n",
X`009`009`009    rlptr->tg_name->mt_text, targetname);
X`009`009}
X
X`009`009ruledep = E_ALLOC(MAKE_TOKEN);
X`009`009ruledep->mt_text = newstr(rlsrcname, -1);
X`009`009ruledep->mt_simple = TRUE;
X`009`009new_copy_token(rlptr->tg_dep, &ruledep->mt_next, NULL);
X`009`009if (tptr == NULL) {
X`009`009`009tptr = E_ALLOC(MAKE_TARGET);
X`009`009`009tptr->tg_name = E_ALLOC(MAKE_TOKEN);
X`009`009`009tptr->tg_name->mt_text = newstr(targetname, -1);
X`009`009`009tptr->tg_name->mt_next = NULL;
X`009`009`009tptr->tg_dep = NULL;
X`009`009`009tptr->tg_next = NULL;
X`009`009} else {
X`009`009`009saveptr = tptr->tg_next;
X`009`009`009tptr->tg_next = NULL;
X`009`009`009new_copy_target(tptr, &newptr);
X`009`009`009tptr->tg_next = saveptr;
X`009`009`009tptr = newptr;
X`009`009}
X`009`009merge_dep(ruledep, &tptr->tg_dep, NULL);
X`009`009new_copy_cmd(rlptr->tg_cmd, &tptr->tg_cmd);
X
X`009`009/* Expand any macros in the dependency list. */
X
X`009`009if (expand_special(tptr->tg_dep, tptr->tg_name, NULL) ==
X`009`009    EXP_ERROR) {
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_expandrule, 6,
X`009`009`009    FAO_AD("dependencies"),
X`009`009`009    FAO_AD(rlptr->tg_name->mt_text),
X`009`009`009    FAO_AD(targetname));
X#else
V`009`009`009printf("  ** Error expanding dependencies of rule %s for target %s\
Xn",
X`009`009`009    rlptr->tg_name->mt_text, targetname);
X#endif
X`009`009`009return (UPDT_ERROR);
X`009`009}
X`009`009char_process(tptr->tg_dep, FALSE);
X
X`009`009/* Expand special macros in update commands using
X`009`009the current target name and dependency list. */
X
X`009`009if (cmd_exp_special(tptr->tg_cmd,
X`009`009    tptr->tg_name, tptr->tg_dep) == EXP_ERROR) {
X#ifdef VMS_ERROR
X`009`009`009lib$signal(&Mak_expandrule, 6,
X`009`009`009    FAO_AD("commands"),
X`009`009`009    FAO_AD(rlptr->tg_name->mt_text),
X`009`009`009    FAO_AD(targetname));
X#else
X`009`009`009printf("  ** Error expanding commands of rule %s for target %s\n",
X`009`009`009    rlptr->tg_name->mt_text, targetname);
X#endif
X`009`009`009return (UPDT_ERROR);
X`009`009}
X`009}
X
X`009/* If a command file is being created, assume the target is out of date. */
X
X`009if (Bldfile != NULL) {
X`009`009remake_target = TRUE;
X`009}
X
X`009/* Update the dependencies, if any, and see if the current
X`009target is out of date with respect to any of them. */
X
X`009for (dptr = tptr->tg_dep; dptr != NULL; dptr = dptr->mt_next) {
X
X`009`009/* Check for blank dependencies caused by
X`009`009macros which are defined as nothing. */
X
X`009`009if (dptr->mt_text == NULL || *dptr->mt_text == '\0') {
X`009`009`009continue;
X`009`009}
X
X`009`009/* State which dependency is being done now. */
X
X`009`009if (Verbose) {
X`009`009`009print_prefix();
X`009`009`009printf("%s depends on %s\n",
X`009`009`009    targetname, dptr->mt_text);
X`009`009}
X
X`009`009/* Update the dependency. */
X
X`009`009Update_level += UPDATE_INDENT;
X`009`009strecpy(dptr->mt_text, updtname);
X`009`009if (update(updtname, rlist, tlist) != UPDT_OKAY) {
X`009`009`009Update_level -= UPDATE_INDENT;
X`009`009`009return (UPDT_ERROR);
X`009`009}
X
X`009`009/* If the current target is up to date with respect to
X`009`009all dependencies checked so far, see if it is also up
X`009`009to date with the current dependency. */
X
X`009`009if (!remake_target) {
X`009`009`009dep_exists = (get_file_date(dptr->mt_text,
X`009`009`009    &depdate) >= 0);
X`009`009`009verbose_show(dptr->mt_text, dep_exists, &depdate);
X`009`009`009if (out_of_date(&depdate, &targdate)) {
X`009`009`009`009if (Verbose) {
X`009`009`009`009`009print_prefix();
X`009`009`009`009`009printf("%s is out of date with respect to %s\n",
X`009`009`009`009`009    targetname, dptr->mt_text);
X`009`009`009`009}
X`009`009`009`009remake_target = TRUE;
X`009`009`009} else if (Verbose) {
X`009`009`009`009print_prefix();
X`009`009`009`009printf("%s is up to date with respect to %s\n",
X`009`009`009`009    targetname, dptr->mt_text);
X`009`009`009}
X`009`009}
X`009`009Update_level -= UPDATE_INDENT;
X`009}
X
X`009/* If target does not exist, or if it is out of date with
X`009respect to one or more of its dependencies, execute commands
X`009to re-create it. */
X
X`009if (remake_target) {
X`009`009if (Verbose) {
X`009`009`009print_prefix();
X`009`009`009printf("Recreating %s ...\n", targetname);
X`009`009}
X`009`009Curr_targ = targetname;
X`009`009if (Touch) {
X`009`009`009touch_file(targetname);
X`009`009} else if (do_cmd(tptr->tg_cmd) != NORMAL_EXIT) {
X`009`009`009if (!precious(targetname)) {
X`009`009`009`009if (Verbose) {
X`009`009`009`009`009print_prefix();
X`009`009`009`009`009printf("Error during creation of %s; deleting it.\n",
X`009`009`009`009`009    targetname);
X`009`009`009`009}
X`009`009`009`009delete_file(targetname);
X`009`009`009}
X`009`009`009return (UPDT_ERROR);
X`009`009}
X`009`009Curr_targ = NULL;
X`009`009if (Verbose) {
X`009`009`009print_prefix();
X`009`009`009printf("%s updated.\n", targetname);
X`009`009}
X`009} else if (Verbose || Update_level == 0) {
X`009`009if (Update_level != 0) {
X`009`009`009print_prefix();
X`009`009}
X`009`009printf("%s is up to date.\n", targetname);
X`009}
X`009return (UPDT_OKAY);
X}
X
X/* Search for the named target in a list of MAKE_TARGETs. */
X
Xstatic
XMAKE_TARGET *
Xfind_target(tname, tlist)
Xchar *tname;
Xregister MAKE_TARGET *tlist;
X{
X`009while (tlist != NULL && COMPARE(tname, tlist->tg_name->mt_text)) {
X`009`009tlist = tlist->tg_next;
X`009}
X`009return (tlist);
X}
X
X/* Execute a list of commands to update a target.  If a command exits with
Xan error status, and the command flags do not include CMD_IGN_ERROR, stop
Xexecuting commands and return the error status.  If all commands complete
Xsuccessfully, return the standard exit status. */
X
Xstatic
Xdo_cmd(cmdptr)
Xregister MAKE_COMMAND *cmdptr;
X{
X`009int status;
X`009char cmd_text[MAX_CMD_LEN + 1];
X
X`009while (cmdptr != NULL) {
X`009`009status = tok2str(cmdptr->cmd_word,
X`009`009    cmd_text, sizeof(cmd_text) - 1);
X
X`009`009if (Bldfile != NULL) {
X`009`009`009fprintf(Bldfile, "$ %s\n", cmd_text);
X`009`009`009cmdptr = cmdptr->cmd_next;
X`009`009`009continue;
X`009`009}
X
X`009`009if (!Silent && !(cmdptr->cmd_flag & CMD_NO_ECHO)) {
X`009`009`009printf("%c\t%s\n", (Active ? ' ' : CLI_COMMENT),
X`009`009`009    cmd_text);
X`009`009}
X
X`009`009if (Active) {
X`009`009`009if (Ignore || (cmdptr->cmd_flag & CMD_IGN_ERROR)) {
X`009`009`009`009if (!Sub_ign_err) {
X`009`009`009`009`009sub_write("on severe then continue");
X`009`009`009`009`009Sub_ign_err = TRUE;
X`009`009`009`009}
X`009`009`009} else {
X`009`009`009`009if (Sub_ign_err) {
X`009`009`009`009`009sub_write("on warning then stop/id=0");
X`009`009`009`009`009Sub_ign_err = FALSE;
X`009`009`009`009}
X`009`009`009}
X`009`009`009sub_write(cmd_text);
X`009`009`009if (~cmdptr->cmd_flag & CMD_CONTINUE) {
X`009`009`009`009sub_write("");
X`009`009`009}
X`009`009}
X
X`009`009cmdptr = cmdptr->cmd_next;
X`009}
X`009return (NORMAL_EXIT);
X}
X
X/* This routine returns TRUE if the named target is "precious".  A precious
Xtarget is a target which is named as a dependency of the special target
X.PRECIOUS in the makefile.  This feature is not yet implemented, so this
Xroutine always returns FALSE. */
X
Xprecious(tname)
Xchar *tname;
X{
X`009return (FALSE);
X}
X
X/* Concatenate the text of each token in a list to form a single string.
XReturn the length of the string. */
X
Xstatic
Xtok2str(tokptr, str, maxlen)
Xregister MAKE_TOKEN *tokptr;
Xregister char *str;
Xint maxlen;
X{
X`009char *str0;
X
X`009str0 = str;
X`009while (tokptr != NULL &&
X`009    str - str0 + strlen(tokptr->mt_text) < maxlen) {
X`009`009str = strecpy(tokptr->mt_text, str);
X`009`009tokptr = tokptr->mt_next;
X`009`009if (tokptr != NULL) {
X`009`009`009*str++ = ' ';
X`009`009}
X`009}
X`009*str = '\0';
X`009return (str - str0);
X}
X
X/* If verbose mode is on, describe a file and its modify time. */
X
Xstatic
Xverbose_show(fname, file_exists, fdateptr)
Xchar *fname;
Xint file_exists;
XDATE *fdateptr;
X{
X`009if (Verbose) {
X`009`009print_prefix();
X`009`009printf("%s ", fname);
X`009`009if (file_exists) {
X`009`009`009printf("%sed ", (Created ? "creat" : "revis"));
X`009`009`009printdate(fdateptr);
X`009`009`009putchar('\n');
X`009`009} else {
X`009`009`009printf("does not exist.\n");
X`009`009}
X`009}
X}
X
X/* This routine searches the default rules list for rules which can be used to
Xcreate the current target file.  If such a rule is found, and the dependency
Xfile (the file from which the target is created) exists, it puts the modify
Xdate of the dependency file in the date buffer pointed to by dateptr, and
Xreturns a pointer to the default rule being used.  If no default rule can
Xbe used, it returns NULL. */
X
Xstatic
XMAKE_TARGET *
Xusable_rule(rlist, tname, sname, dateptr)
Xregister MAKE_TARGET *rlist;
Xchar *tname, *sname;
XDATE *dateptr;
X{
X`009register char *rule_to, *src_end, *targtype;
X
X`009/* Set up empty name. */
X
X`009if (sname != NULL) {
X`009`009*sname = '\0';
X`009}
X
X`009/* Determine what kind of file the target is. */
X
X`009if ((targtype = find_file_type(tname)) == NULL) {
X`009`009return (NULL);
X`009}
X
X`009/* Check each rule for one which might apply. */
X
X`009for (; rlist != NULL; rlist = rlist->tg_next) {
X`009`009rule_to = find_file_type(&rlist->tg_name->mt_text[1]);
X`009`009if (rule_to != NULL && COMPARE(rule_to, targtype) == 0) {
X`009`009`009strecpy(tname, sname);
X`009`009`009src_end = &sname[targtype - tname + 1];
X`009`009`009rule_to = &rlist->tg_name->mt_text[1];
X`009`009`009do {
X`009`009`009`009*src_end++ = *rule_to++;
X`009`009`009} while (*rule_to && *rule_to != '.');
X`009`009`009*src_end = '\0';
X`009`009`009if (get_file_date(sname, dateptr) >= 0) {
X`009`009`009`009return (rlist);
X`009`009`009}
X`009`009}
X`009}
X`009return (NULL);
X}
$ GoSub Convert_File
$ File_is="MKSCAN.C"
$ Check_Sum_is=1077656066
$ 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
Xmkscan.c
XThis file contains the code to perform lexical analysis of makefiles.
X*/
X
X#include "make.h"
X
X#define MAX_CMD_DEF_LEN`009256
X
X#define`009_GETC(fp)`009((fp) == NULL ? cmdln_getc() : getc(fp))
X#define _UNGETC(c, fp)`009((fp) == NULL ? cmdln_ungetc(c) : ungetc(c, (fp)))
X
Xstatic int Line_number;`009`009/* position in current makefile */
Xstatic int Prev_nl;`009`009/* true if at start of line */
Xstatic int Cmdlndef;`009`009/* command line definition status */
Xstatic char Cmdbuf[MAX_CMD_DEF_LEN];
Xstatic int Cmdidx;
X
X/* Read a makefile and parse it into tokens significant to make. */
X
XMAKE_TOKEN *
Xmake_token_list(mkfptr)
XFILE *mkfptr;
X{
X`009int toklen, tokline, simple;
X`009register MAKE_TOKEN **mtptr;
X`009MAKE_TOKEN *tok_list_ptr;
X`009char token[MAX_TOKEN_LEN];
X
X`009/* Scan through the makefile. */
X
X`009Line_number = 1;
X`009Prev_nl = TRUE;
X`009mtptr = &tok_list_ptr;
X`009*mtptr = NULL;
X`009while ((toklen = make_token(mkfptr, token, &tokline, &simple)) != EOF) {
X`009`009*mtptr = E_ALLOC(MAKE_TOKEN);
X`009`009(*mtptr)->mt_text = newstr(token, toklen);
X`009`009(*mtptr)->mt_line = tokline;
X`009`009(*mtptr)->mt_next = NULL;
X`009`009(*mtptr)->mt_simple = simple;
X`009`009mtptr = &(*mtptr)->mt_next;
X`009}
X`009return (tok_list_ptr);
X}
X
X/* Read next token from makefile pointed to by mkfptr.
XReturn length of token, or EOF on end of file. */
X
Xmake_token(mkfptr, tokbuf, tklnptr, simptr)
XFILE *mkfptr;
Xregister char *tokbuf;
Xint *tklnptr, *simptr;
X{
X`009static int start_of_line = TRUE;
X`009static int command_line = FALSE;
X`009static int tok_ch_saved = EOF;
X`009static int past_first_comm_tok = FALSE;
X`009register int c;
X`009int tok_got_literal;
X`009char *tok0;
X
X`009/* Get line number before reading first character of token. */
X
X`009*simptr = TRUE;
X`009*tklnptr = Line_number;
X
X`009/* Read first character of token. */
X
X`009if (tok_ch_saved != EOF) {
X`009`009c = tok_ch_saved;
X`009`009tok_ch_saved = EOF;
X`009} else if ((c = make_getc(mkfptr)) == EOF) {
X`009`009command_line = FALSE;
X`009`009return (EOF);
X`009}
X
X`009/* Check for end of line. */
X
X`009if (c == '\n') {
X`009`009start_of_line = TRUE;
X`009`009command_line = FALSE;
X`009`009tokbuf[0] = c;
X`009`009tokbuf[1] = '\0';
X`009`009return (1);
X`009}
X
X`009/* If token wasn't newline, get line number now, since original
X`009line number may be off due to line continuation, multiple newlines,
X`009or other unpredictable acts of god. */
X
X`009*tklnptr = Line_number;
X
X`009/* Check for single character tokens. */
X
X`009if (IS_TOK_SEP(c)) {
X
X`009`009/* Of the token separator characters, space is significant at
X`009`009the start of a line.  The others are significant anywhere. */
X
X`009`009if (c != ' ' || start_of_line) {
X`009`009`009start_of_line = FALSE;
X`009`009`009command_line = (c == ' ');
X`009`009`009past_first_comm_tok = FALSE;
X`009`009`009tokbuf[0] = c;
X`009`009`009tokbuf[1] = '\0';
X`009`009`009return (1);
X`009`009}
X
X`009`009/* If the space is not at the start of a line, it
X`009`009is a token separator, with no other significance,
X`009`009and can therefore be skipped. */
X
X`009`009c = make_getc(mkfptr);
X`009}
X
X`009/* Check for special characters on command lines. */
X
X`009if (command_line && (c == MC_IGN_ERROR || c == MC_NO_ECHO) &&
X`009    !past_first_comm_tok) {
X`009`009*tokbuf++ = c;
X`009`009*tokbuf = '\0';
X`009`009return (1);
X`009}
X
X`009/* Get next word, of any length. */
X
X`009tok0 = tokbuf;
X`009start_of_line = FALSE;
X`009tok_got_literal = FALSE;
X`009while (1) {
X`009`009if (c == INVOKE_MACRO && !tok_got_literal) {
X`009`009`009*simptr = FALSE;
X`009`009}
X`009`009*tokbuf++ = c;
X`009`009if ((c = make_getc(mkfptr)) == EOF) {
X`009`009`009break;
X`009`009}
X`009`009if (IS_TOK_SEP(c)) {
X`009`009`009if (command_line) {
X`009`009`009`009if (c != DEPENDENCY && c != DEFINE_MACRO) {
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009} else {
X`009`009`009`009if (!tok_got_literal || c == '\n') {
X`009`009`009`009`009break;
X`009`009`009`009}
X`009`009`009}
X`009`009}
X`009`009tok_got_literal = (!tok_got_literal && c == NEXT_LITERAL);
X`009}
X`009tok_ch_saved = c;
X`009*tokbuf = '\0';
X`009past_first_comm_tok = TRUE;
X`009return (tokbuf - tok0);
X}
X
X/* Return the next syntactically significant character
Xfrom the makefile pointed to by mkfptr. */
X
Xmake_getc(mkfptr)
XFILE *mkfptr;
X{
X`009static int make_saved = EOF;
X`009static int mkget_literal = FALSE;
X`009int c;
X
X`009while (1) {
X
X`009`009/* Get next character from file. */
X
X`009`009if (make_saved != EOF) {
X`009`009`009c = make_saved;
X`009`009`009make_saved = EOF;
X`009`009} else {
X`009`009`009c = map_getc(mkfptr);
X`009`009}
X
X`009`009/* Compress consecutive blanks into one. */
X
X`009`009if (c == ' ') {
X`009`009`009while (1) {
X
X`009`009`009`009/* Compress any number of blanks into one. */
X
X`009`009`009`009while ((c = map_getc(mkfptr)) == ' ')
X`009`009`009`009`009;
X
X`009`009`009`009/* If newline follows blanks, return it,
X`009`009`009`009thereby deleting past trailing blanks. */
X
X`009`009`009`009if (c == '\n') {
X`009`009`009`009`009break;
X`009`009`009`009}
X
X`009`009`009`009/* Check for line continuation. */
X
X`009`009`009`009if (c == CONTINUE_LINE) {
X`009`009`009`009`009if ((c = map_getc(mkfptr)) == '\n') {
X`009`009`009`009`009`009continue;
X`009`009`009`009`009}
X`009`009`009`009`009make_saved = CONTINUE_LINE;
X`009`009`009`009}
X
X`009`009`009`009/* Save char after CONTINUE_LINE. */
X
X`009`009`009`009_UNGETC(c, mkfptr);
X`009`009`009`009c = ' ';
X`009`009`009`009break;
X`009`009`009}
X`009`009}
X
X`009`009/* Check for line continuation. */
X
X`009`009if (!mkget_literal && c == CONTINUE_LINE) {
X`009`009`009if ((c = map_getc(mkfptr)) == '\n') {
X`009`009`009`009continue;
X`009`009`009}
X`009`009`009_UNGETC(c, mkfptr);
X`009`009`009c = CONTINUE_LINE;
X`009`009}
X
X`009`009/* Remember each literal that's returned. */
X
X`009`009mkget_literal = (!mkget_literal && c == NEXT_LITERAL);
X
X`009`009/* Eat consecutive newlines. */
X
X`009`009if (c != '\n' || !Prev_nl) {
X`009`009`009Prev_nl = (c == '\n');
X`009`009`009return (c);
X`009`009}
X`009}
X}
X
X/* Read characters from mkfptr.  Convert any tabs into spaces.
XEliminate comments. */
X
Xmap_getc(mkfptr)
XFILE *mkfptr;
X{
X`009register int c;
X
X`009/* Read next character, check for comment. */
X
X`009if ((c = _GETC(mkfptr)) == OPEN_COMMENT) {
X`009`009while ((c = _GETC(mkfptr)) != EOF && c != CLOSE_COMMENT) {
X`009`009`009if (c == '\n') {
X`009`009`009`009Line_number++;
X`009`009`009}
X`009`009}
X`009`009c = REPLACE_COMMENT;
X`009}
X
X`009/* If character is NEXT_LITERAL, and following character is
X`009OPEN_COMMENT, delete NEXT_LITERAL and return OPEN_COMMENT. */
X
X`009if (c == NEXT_LITERAL) {
X`009`009if ((c = _GETC(mkfptr)) != OPEN_COMMENT) {
X`009`009`009_UNGETC(c, mkfptr);
X`009`009`009c = NEXT_LITERAL;
X`009`009}
X`009}
X
X`009/* Blanks require special checks on the following character. */
X
X`009if (c == ' ' || c == '\t') {
X
X`009`009/* Compress any number of consecutive blanks into one. */
X
X`009`009while ((c = _GETC(mkfptr)) == ' ' || c == '\t')
X`009`009`009;
X
X`009`009/* Blanks preceding a comment are irrelevant. */
X
X`009`009if (c == OPEN_COMMENT) {
X`009`009`009while ((c = _GETC(mkfptr)) != EOF && c != CLOSE_COMMENT) {
X`009`009`009`009if (c == '\n') {
X`009`009`009`009`009Line_number++;
X`009`009`009`009}
X`009`009`009}
X`009`009`009c = REPLACE_COMMENT;
X`009`009}
X
X`009`009/* Eliminate trailing spaces on a line. */
X
X`009`009if (c != '\n' && c != EOF) {
X`009`009`009if (c != ' ') {
X`009`009`009`009_UNGETC(c, mkfptr);
X`009`009`009}
X`009`009`009c = ' ';
X`009`009}
X`009}
X
X`009/* Keep track of line number. */
X
X`009if (c == '\n') {
X`009`009Line_number++;
X`009}
X`009return (c);
X}
X
X/* Return the next character from command line macro definitions. */
X
Xcmdln_getc()
X{
X`009int len;
X
X`009/* See if command line macro definitions were given. */
X
X`009if (Cmdlndef == 0) {
X`009`009if (cli_present("DEFINE") > 0) {
X`009`009`009Cmdlndef = 1;
X`009`009`009Cmdbuf[Cmdidx = 0] = '\0';
X`009`009} else {
X`009`009`009Cmdlndef = -1;
X`009`009`009return (EOF);
X`009`009}
X`009} else if (Cmdlndef < 0) {
X`009`009return (EOF);
X`009}
X
X`009/* Return the next character. */
X
X`009if (Cmdbuf[Cmdidx] == '\0') {
X`009`009len = cli_get_value("DEFINE", Cmdbuf, sizeof(Cmdbuf) - 1);
X`009`009if (len < 0) {
X`009`009`009Cmdlndef = -1;
X`009`009} else {
X`009`009`009Cmdidx = 0;
X`009`009}
X`009`009if (Cmdlndef == 0) {
X`009`009`009Cmdlndef = 1;
X`009`009} else {
X`009`009`009return ('\n');
X`009`009}
X`009}
X`009return (Cmdbuf[Cmdidx++] & 0xFF);
X}
X
X/* Make character c the next character to be returned
Xwhen reading command line macro definitions. */
X
Xcmdln_ungetc(c)
Xint c;
X{
X`009if (Cmdidx > 0) {
X`009`009Cmdbuf[--Cmdidx] = (c == '\n' ? '\0' : c);
X`009}
X}
$ GoSub Convert_File
$ Goto Part8