/* **++ ** FACILITY: MMK ** ** ABSTRACT: Description file reader ** ** MODULE DESCRIPTION: ** ** This module contains the read_description routine and its ** supporting routines. ** ** AUTHOR: M. Madison ** ** Copyright (c) 2008, Matthew Madison. ** Copyright (c) 2013, Endless Software Solutions. ** ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** * Redistributions of source code must retain the above ** copyright notice, this list of conditions and the following ** disclaimer. ** * Redistributions in binary form must reproduce the above ** copyright notice, this list of conditions and the following ** disclaimer in the documentation and/or other materials provided ** with the distribution. ** * Neither the name of the copyright owner nor the names of any ** other contributors may be used to endorse or promote products ** derived from this software without specific prior written ** permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** CREATION DATE: 20-AUG-1992 ** ** MODIFICATION HISTORY: ** ** 20-AUG-1992 V1.0 Madison Initial coding. ** 01-SEP-1992 V1.1 Madison Comments. ** 02-APR-1993 V1.2 Madison Don't strip comments off commands. ** 04-JUN-1993 V1.3 Madison Support for .INCLUDE files. ** 22-OCT-1993 V1.3-1 Madison Trim trailing blanks off lines. ** 12-DEC-1993 V1.4 Madison Add Fill_In_Missing_Sources. ** 01-JUL-1994 V1.5 Madison Support for CMS. ** 06-JUL-1994 V1.5-1 Madison Add filename to default filespecs. ** 14-JUL-1994 V1.6 Madison Update for prefixed inferences. ** 15-JUL-1994 V1.6-1 Madison Fix broken CMS fallback logic. ** 29-DEC-1994 V1.6-2 Madison Allow for comment lines with leading blanks. ** 12-JAN-1995 V1.6-3 Madison Defer target-libmod dependencies. ** 19-JAN-1995 V1.6-4 Madison Fill in missing sources only when there ** are no explicit action lines. ** 27-JUN-1995 V1.6-5 Madison CMS lib elements vs. file specs. ** 21-JUL-1995 V1.6-6 Madison Fix negative index into xbuf. ** 03-OCT-1995 V1.6-7 Madison Fix handling of continuations! ** 21-FEB-1996 V1.6-8 Madison Fix bound of loop in strip_comments. ** 27-DEC-1998 V1.7 Madison General cleanup. ** 20-JAN-2001 V1.8 Madison Fixes for rule use, per Chuck Lane. ** 30-MAR-2001 V1.8-1 Madison Fix for prefixed rules, per Chuck Lane. ** 08-APR-2001 V1.8-2 Madison Fix Find_Usable_Object to scan the ** source list for a match before ** inferring any dependencies. Also change ** name comparisons to be case-blind. ** 11-JUL-2002 V1.8-3 Madison Have Fill_In_Missing_Sources walk the ** double-colon dependency list, too. ** 07-AUG-2002 V1.8-4 Madison Fixed basename match bug in Find_Usable_Object. ** 09-DEC-2003 V1.8-5 Madison CMS objects aren't always usable. ** 02-MAR-2008 V1.9 Madison Make base-name-match the default; fetch ** newer makefiles out of CMS, if present. ** 30-SEP-2009 V1.10 Sneddon Added MMSDESCRIPTION_FILE. ** 07-APR-2010 V1.10-1 Sneddon Got ahead of myself with symbols. Changed ** MMSDESCRIPTION_FILE to MMK_K_SYM_BUILTIN. ** 12-JUL-2012 V1.11 Sneddon Tweak strip_comments to support '!='. ** 09-APR-2013 V1.12 Sneddon #57. Fix to support default filespec ** correctly in Read_Description. ** 01-MAY-2013 V1.13 Sneddon #68, undo V1.12 changes. **-- */ #pragma module READDESC "V1.13" #include <ctype.h> #include "mmk.h" #include "globals.h" #include <rmsdef.h> struct IO { struct IO *flink, *blink; char *linebuf, *stripbuf; FILEHANDLE unit; int maxlen, current_line; char filespec[256]; }; /* ** Forward declarations */ void Read_Description(char *, char *, int); static void strip_comments(char *, char *); static void Process_Deferred_Dependencies(void); static void Fill_In_Missing_Sources(void); static int Find_Usable_Object(struct SFX *, struct DEPEND *); /* **++ ** ROUTINE: Read_Description ** ** FUNCTIONAL DESCRIPTION: ** ** Reads in a description file and forms lines that will be ** parsed by parse_descrip. Handles continuation lines, strips ** comments. ** ** Although rules files and description files use identical ** syntax, the third argument is a flag to indicate that this is ** a rules file, since error handling is different in that case. ** ** RETURNS: void (errors are signaled) ** ** PROTOTYPE: ** ** Read_Description(char *fspec, char *defspec, int rules_file) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** ** ** SIDE EFFECTS: None. ** **-- */ void Read_Description (char *fspec, char *defspec, int rules_file) { struct QUE ioque; struct IO *io; char resspec[256]; char *buf, *xbuf; FILEHANDLE unit; unsigned int status, cstatus; TIME frdt, crdt, junktime; int bufsize, len; int continuation, maxlen, xlen, itry; char element[256]; static char *tryfile[] = {"SYS$DISK:[]DESCRIP.MMS", "SYS$DISK:[]MAKEFILE."}; if (*fspec == '\0') { for (itry = 0; itry < 2; itry++) { status = file_get_rdt(tryfile[itry], &frdt); if (use_cms) { extract_nametype(element, tryfile[itry]); cstatus = cms_get_rdt(element, 0, &crdt); if (OK(cstatus)) { if (!OK(status) || (!(frdt.long1 == crdt.long1 && frdt.long2 == crdt.long2) && OK(lib$sub_times(&crdt, &frdt, &junktime)))) { status = cms_fetch_file(element, tryfile[itry]); } } } if (OK(status)) { strcpy(fspec, tryfile[itry]); break; } } } else { status = file_get_rdt(fspec, &frdt); if (use_cms) { extract_nametype(element, fspec); cstatus = cms_get_rdt(element, 0, &crdt); if (OK(cstatus)) { if (!OK(status) || (!(frdt.long1 == crdt.long1 && frdt.long2 == crdt.long2) && OK(lib$sub_times(&crdt, &frdt, &junktime)))) status = cms_fetch_file(element, fspec); } } else { status = SS$_NORMAL; } } if (OK(status) && *fspec != '\0') status = file_open(fspec, &unit, defspec, resspec, &maxlen); if (!OK(status)) { if (rules_file) { lib$signal(MMK__NOOPNRUL, 1, fspec, status); if (OK(exit_status)) exit_status = MMK__NOOPNRUL; } else { lib$signal(MMK__NOOPNDSC, 1, fspec, status); } return; } if (!rules_file) { Define_Symbol(MMK_K_SYM_BUILTIN, "MMSDESCRIPTION_FILE", resspec, strlen(resspec)); } if (do_log) { lib$signal((rules_file ? MMK__OPENRULE : MMK__OPENDESC), 1, resspec); } ioque.head = ioque.tail = &ioque; io = malloc(sizeof(struct IO)); io->unit = unit; io->maxlen = maxlen; io->linebuf = malloc(io->maxlen+1); io->stripbuf = malloc(io->maxlen+1); io->current_line = 0; strcpy(io->filespec, resspec); queue_insert(io, ioque.tail); buf = (char *) 0; bufsize = 0; unit = 0; while (queue_remove(ioque.head, &io)) { while (OK(file_read(io->unit, io->linebuf, io->maxlen+1, &len))) { io->current_line++; while (len > 0 && isspace(io->linebuf[len-1])) len--; io->linebuf[len] = '\0'; if (continuation && isspace(*io->linebuf)) { char *cp; for (cp = io->linebuf; *cp; cp++) if (!isspace(*cp)) break; strip_comments(io->stripbuf, cp-1); xbuf = io->stripbuf; } else if (isspace(*io->linebuf)) { char *cp; for (cp = io->linebuf; *cp; cp++) if (!isspace(*cp)) break; if (*cp == '!' || *cp == '#') *io->linebuf = '\0'; xbuf = cp - 1; } else { strip_comments(io->stripbuf, io->linebuf); xbuf = io->stripbuf; } xlen = strlen(xbuf); continuation = 0; if (xlen > 0) { if (xbuf[xlen-1] == '-' || xbuf[xlen-1] == '\\') { continuation = 1; xbuf[xlen-1] = '\0'; xlen -= 1; } if (bufsize > 0) { buf = realloc(buf, bufsize+xlen); strcpy(buf+bufsize-1,xbuf); bufsize += xlen; } else { bufsize = xlen + 1; buf = malloc(bufsize); strcpy(buf, xbuf); } } if (!continuation && bufsize > 1) { parse_descrip(buf, bufsize-1, &unit, &maxlen, io->current_line, io->filespec); free(buf); bufsize = 0; if (unit) { queue_insert(io, &ioque); io = malloc(sizeof(struct IO)); io->unit = unit; io->maxlen = maxlen; io->linebuf = malloc(io->maxlen+1); io->stripbuf = malloc(io->maxlen+1); io->current_line = 0; file_get_filespec(unit, io->filespec, sizeof(io->filespec)); unit = 0; } } } if (bufsize > 0) { parse_descrip(buf, bufsize-1, &unit, &maxlen, io->current_line, io->filespec); free(buf); if (unit) { struct IO *io; io = malloc(sizeof(struct IO)); io->unit = unit; io->maxlen = maxlen; io->linebuf = malloc(io->maxlen+1); io->stripbuf = malloc(io->maxlen+1); io->current_line = 0; file_get_filespec(unit, io->filespec, sizeof(io->filespec)); unit = 0; queue_insert(io, &ioque); } } file_close(io->unit); free(io->linebuf); free(io->stripbuf); free(io); } Process_Deferred_Dependencies(); Fill_In_Missing_Sources(); } /* **++ ** ROUTINE: strip_comments ** ** FUNCTIONAL DESCRIPTION: ** ** Strips comments from the end of a string. A comment begins ** with either an exclamation point (!) or a pound sign (#). Quoted ** strings are handled properly. ** ** RETURNS: void ** ** PROTOTYPE: ** ** strip_comments(char *dest, char *source) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ static void strip_comments (char *dest, char *source) { int quote; register char *cp=source, *cp1=dest; quote = 0; for (cp = source; *cp; cp++) { if (quote) { if (*cp == '"') quote = !quote; } else { if (*cp == '!' || *cp == '#') break; } *cp1++ = *cp; } while (cp1 > dest && isspace(*(cp1-1))) cp1--; *cp1 = 0; } /* **++ ** ROUTINE: Process_Deferred_Dependencies ** ** FUNCTIONAL DESCRIPTION: ** ** Looks for dependencies that we created for library modules ** that are targets, and adds them to the main dependencies queue if ** there aren't dependencies there already for them. The deferred ** dependencies list is created by Parse_Objects(). ** ** RETURNS: void ** ** PROTOTYPE: ** ** Process_Deferred_Dependencies() ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ static void Process_Deferred_Dependencies (void) { struct DEPEND *dep; while (queue_remove(dep_deferred.flink, &dep)) { if (find_dependency(dep->target, 0) == 0) queue_insert(dep, dependencies.blink); else mem_free_depend(dep); } } /* Process_Deferred_Dependencies */ /* **++ ** ROUTINE: Fill_In_Missing_Sources ** ** FUNCTIONAL DESCRIPTION: ** ** Add implied sources to dependency rules missing them. ** ** RETURNS: void ** ** PROTOTYPE: ** ** Fill_In_Missing_Sources() ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ static void Fill_In_Missing_Sources (void) { struct DEPEND *dep, *dep2, *prev; struct OBJREF *o, *o2; struct SFX *s; /* ** We only need to fill in sources when the target is a file, ** there are no explicit action lines for the dependency, and ** the existing sources do not already match a rule (checked in ** Find_Usable_Object). */ for (dep = dependencies.flink; dep != &dependencies; dep = dep->flink) { if (dep->double_colon) { /* ** Treat extra double-colon dependencies with no commands as if ** they were just additions to the first dependency. */ dep2 = dep->dc_flink; prev = dep; while (dep2 != 0) { if (dep2->cmdqptr == 0 || (dep2->cmdqptr->flink == dep2->cmdqptr)) { while (queue_remove(dep2->sources.flink, &o)) { for (o2 = dep->sources.flink; o2 != &dep->sources && o2->obj != o->obj; o2 = o2->flink); if (o2 == &dep->sources) queue_insert(o, dep->sources.blink); else mem_free_objref(o); } prev->dc_flink = dep2->dc_flink; mem_free_depend(dep2); dep2 = prev->dc_flink; } else { prev = dep2; dep2 = dep2->dc_flink; } } } if (dep->target->type != MMK_K_OBJ_FILE) continue; if (dep->cmdqptr != 0 && dep->cmdqptr->flink != dep->cmdqptr) continue; s = find_suffix(dep->target->sfx, -1); if (s != 0) Find_Usable_Object(s, dep); } } /* Fill_In_Missing_Sources */ /* **++ ** ROUTINE: Find_Usable_Object ** ** FUNCTIONAL DESCRIPTION: ** ** ** ** RETURNS: int ** ** PROTOTYPE: ** ** Find_Usable_Object sfx, dep ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ static int Find_Usable_Object (struct SFX *s, struct DEPEND *dep) { struct SFX *s2; struct RULE *r, *xr; struct OBJECT *obj, *obj2; struct OBJREF *o; struct DEPEND *dep2; char trgnam[MMK_S_FILE], tfile[MMK_S_FILE], sfile[MMK_S_FILE]; char trgbase[MMK_S_FILE]; int trgnamlen, tfilelen, check_cms, slen, baselen; /* ** Don't use the CMS suffixes if we're not using CMS */ slen = strlen(s->value); check_cms = s->value[slen-1] == '~'; if (check_cms && !use_cms) return 0; tfilelen = extract_name(tfile, dep->target->name); /* ** Look through the sources first to see if there is a matching rule #ifndef BASE_NAME_MATCH_NOT_REQUIRED ** _and_ there is a base name match. #endif */ baselen = extract_filename(trgbase, dep->target->name); for (o = dep->sources.flink; o != &dep->sources; o = o->flink) { if (o->obj->type != MMK_K_OBJ_FILE) continue; r = find_rule_with_prefixes(dep->target, o->obj); if (r == 0) continue; #ifndef BASE_NAME_MATCH_NOT_REQUIRED trgnamlen = extract_filename(sfile, o->obj->name); if (baselen == trgnamlen && strneql_case_blind(trgbase, sfile, trgnamlen)) #endif return 1; } /* ** Go through suffixes, looking for one that will (a) get us to ** the desired suffix, and (b) has a source file. If (a) and not ** (b) then recurse to find how to get the intermediate suffix ** (implicit rule chaining). */ for (s2 = s->flink; s2 != &suffixes; s2 = s2->flink) { /* ** Skip over any CMS element suffixes if we aren't using CMS on this build. */ if (!use_cms) { slen = strlen(s2->value); if (s2->value[slen-1] == '~') continue; } /* ** First check for an explicit dependency */ for (dep2 = dependencies.flink; dep2 != &dependencies; dep2 = dep2->flink) { if (dep2->target->type != MMK_K_OBJ_FILE) continue; if (dep2->cmdqptr == 0 || dep2->cmdqptr->flink == dep2->cmdqptr) continue; if (strcmp(dep2->target->sfx, s2->value) != 0) continue; trgnamlen = extract_name(sfile, dep2->target->name); if (tfilelen != trgnamlen || !strneql_case_blind(tfile, sfile, tfilelen)) continue; obj = mem_get_object(); memcpy(obj->name, tfile, trgnamlen); strcpy(obj->name+trgnamlen, s2->value); strcpy(obj->sfx, s2->value); obj->type = obj->sfx[strlen(obj->sfx)-1] == '~' ? MMK_K_OBJ_CMSFILE : MMK_K_OBJ_FILE; if ((obj2 = Find_Object(obj)) == 0) { Insert_Object(obj); } else { mem_free_object(obj); obj = obj2; } o = mem_get_objref(); o->obj = obj; queue_insert(o, &dep->sources); return 1; } /* ** Find a rule to get from s2 suffix to s suffix */ xr = find_rule(s->value, s2->value); if (xr != 0) { r = scan_rule_list(xr, dep->target->name, 0); if (r == 0) { if (!Find_Usable_Object(s2, dep)) continue; r = scan_rule_list(xr, dep->target->name, 0x10); if (r == 0) continue; /* XXX should never happen */ } obj = mem_get_object(); if (r->srcpfxlen != 0 || r->trgpfxlen != 0) { trgnamlen = extract_filename(trgnam, dep->target->name); memcpy(obj->name, r->srcpfx, r->srcpfxlen); memcpy(obj->name+r->srcpfxlen, trgnam, trgnamlen); strcpy(obj->name+(r->srcpfxlen+trgnamlen), s2->value); } else { memcpy(obj->name, tfile, tfilelen); strcpy(obj->name+tfilelen, s2->value); } strcpy(obj->sfx, s2->value); obj->type = obj->sfx[strlen(obj->sfx)-1] == '~' ? MMK_K_OBJ_CMSFILE : MMK_K_OBJ_FILE; if ((obj2 = Find_Object(obj)) == NULL) { Insert_Object(obj); } else { mem_free_object(obj); obj = obj2; } o = mem_get_objref(); o->obj = obj; queue_insert(o, &dep->sources); return 1; } } return 0; } /* Find_Usable_Object */