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

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

$Part3:
$ File_is="MAKE.RNO"
$ Check_Sum_is=625635744
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X.!
X.! MAKE version 1.1, April 1987
X.! Copyright (C) 1987 by Jesse Perry.
X.! MAKE is in the public domain and may be freely distributed,
X.! used, and modified, provided this notice is not removed.
X.!
X.! make.rno
X.! This file is a tutorial introduction to MAKE.  The HELP entry for
X.! MAKE (MAKE.RNH) refers to this file.  It says that the user can
X.! print a file called MAKE$DOCUMENT for more information.  This file
X.! should therefore be RUNOFF'd and the logical name MAKE$DOCUMENT set
X.! to be full pathname of the .MEM file.
X.!
X.nnm
X.ps 58
X.lm 5
X.rm 75
X.sp 1
X.spr 4
X.fl bo
X.be
X.nfl space
X.b 3
X.c
X^*MAKE TUTORIAL\*
X.b 1
X.ap
X.c
X^*Introduction\*
X Large programs typically have more than one source file.  Each source file
Xis compiled separately.  The resulting object files are then linked together
Xto produce an executable image.  A major advantage of this organization is that
Xwhen changes are made, only the modified source files need to be re-compiled.
XWhen making changes, however, it is easy to forget which source files have
Xbeen modified.  The MAKE program checks the modification date of each file
Xto determine which files must be re-compiled.  (The modification date is
Xby default the file creation date.  The file revision date is used if the
X/REVISED qualifier is given.)
X MAKE divides files into two classes; ^*targets\* and ^*prerequisites\*.
XA target file is a file which is produced by some DCL command.  A
Xprerequisite file for a given target file is a file used as input by the
Xcommand which produces the target file.  For example, if you have a fortran
Xfile called test.for, you can compile it with the FORTRAN command to get an
Xobject file, test.obj. Then test.obj is the target file, and test.for is its
Xprerequisite file, or equivalently, test.obj ^*depends on\* test.for.
X Notice that a file can be both a target ^*and\* a prerequisite.  Test.obj
Xis a target file with prerequisite file test.for, but it is itself a
Xprerequisite file for target test.exe.  Also, a target may have more than
Xone prerequisite file.  For example, if test.for includes test.inc, then
Xboth test.for and test.inc are prerequisites of test.obj.  (Notice, test.inc
Xis ^*not\* a prerequisite of test.for.)
X.b 1
X.c
X^*Dependency Statements\*
X MAKE reads a ^*makefile\*, whose name you specify in the logical name
XMAKE$DEFNAME, or on the command line using the /MAKEFILE qualifier.
XThe makefile tells MAKE what target files you want kept up to date, and
Xwhat prerequisite files each target file depends on.
XThe makefile contains ^*dependency statements\* which describe
Xwhat files depend on what other files, and give the commands needed
Xto update each file when it is out of date with respect to any of its
Xprerequisite files.  A target file is ^*out of date\* with respect
Xto a prerequisite file if the target file does not exist, or if its
Xmodification date is ^*earlier\* than the modification date of the
Xprerequisite file.
X The following is a sample makefile which shows how to specify the
Xexample given above, using MAKE.  (Throughout this document, the sample
Xmakefiles will be set off from ordinary text by "#-----").
X.b 1
X.nf
X#-----
Xtest.obj : test.for
X        fortran test.for
X#-----
X.f
X This makefile contains a single ^*dependency statement\*.
XThe statement says that file test.obj depends on file test.for, and that if
Vtest.obj is out of date with respect to test.for, the command "fortran test.for
X"
Xshould be executed.  The file test.obj is the ^*target\* of the dependency
Xstatement.  Test.for is the only prerequisite of test.obj.  A dependency
Xstatement must have at least one name in the target list (that is, before
Xthe ':').  It may have any number of names in the prerequisite list (after
Xthe ':'), and any number of command lines following the initial
Xtarget/prerequisite line.  The target name in a dependency statement must
Xnot be indented.  The command line(s) which follow the target line must each
Xstart with at least one space or tab.  This leading white space tells
XMAKE that the line contains a command.
X Suppose that in the above example, test.for contains an include statement to
Ximport a file named stuff.inc.  Then test.obj must depend on both test.for
Xand stuff.inc, since if either file is modified, test.obj must be re-compiled
Xto reflect the changes.  A makefile expressing these dependencies is,
X.b 1
X.nf
X#-----
Xtest.obj : test.for stuff.inc
X        fortran test.for
X#-----
X.f
X.bl 1
XNotice that test.obj depends on stuff.inc; test.for ^*does not\*.
X If test.for and subs.for are the source files for a program called
Xtest.exe, then the makefile to keep test.exe up to date is,
X.b 1
X.nf
X#-----
Xtest.exe: test.obj subs.obj
X        link test.obj, subs.obj
Xtest.obj: test.for stuff.inc
X        fortran test.for
Xsubs.obj: subs.for
X        fortran subs.for
X#-----
X.f
X Comments in a makefile can help explain what is being done.
XA MAKE comment starts with a '#' and stops at the end of the line.
XThe above makefile, with some comments, is,
X.b 1
X.nf
X#-----
X# Test.exe is produced from modules test.obj and subs.obj.
Xtest.exe: test.obj subs.obj
X        link test.obj, subs.obj    # link names .exe after first file
X
X# Test.obj depends on stuff.inc because test.for includes it.
Xtest.obj: test.for stuff.inc
X        fortran test.for
X
X# Subs.obj depends only on subs.for.
Xsubs.obj: subs.for
X        fortran subs.for
X#-----
X.f
X MAKE does not, by default, update every target in the makefile.
XIt updates only the targets which are specified on the command line.  If no
Xtargets are named on the command line, it updates the ^*first\* target that
Xit finds in the makefile.  If MAKE is run with no arguments, using the
Xexample makefile just given, it will update test.exe.  Since test.exe
Xdepends on test.obj and subs.obj, MAKE must update these targets before
Xit can update test.exe.  So in this case, MAKE does update all the targets
Xin the makefile, but only because the first target in the makefile depends
Xon all the others.
X Assume there is also a file called test.rno containing documentation for
Xthe program test.exe.  The runoff program converts a .rno file into
Xa .mem file.  Thus, test.mem depends on test.rno.  This dependency can be
Xadded to the makefile, so that MAKE will keep both the program and its
Xdocumentation file up to date.  The makefile to do this is,
X.b 1
X.nf
X#-----
Xtest.exe: test.obj subs.obj
X        link test.obj, subs.obj
Xtest.obj: test.for stuff.inc
X        fortran test.for
Xsubs.obj: subs.for
X        fortran subs.for
X
Xtest.mem: test.rno
X        runoff test.rno
X#-----
X.f
X.b 1
XUsing this makefile, the command "$ MAKE" will update test.exe,
Xwhile the command "$ MAKE TEST.MEM" will update test.mem.
XAny number of targets can be named on the command line, separated
Xby commas.  For example, the command to update both test.exe and test.mem
Xis, "$ MAKE TEST.EXE, TEST.MEM".
X.b 1
X.c
X^*MAKE Macros\*
X Let's consider a different example.  Three files named first.for,
Xsecond.for, and third.for are the source files for a program called
Xlab5.exe.  No include files are used.  The makefile to maintain
Xlab5.exe is,
X.b 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=lab5.exe first.obj, second.obj, third.obj
Xfirst.obj: first.for
X        fortran first.for
Xsecond.obj: second.for
X        fortran second.for
Xthird.obj: third.for
X        fortran third.for
X#-----
X.f
X.b 1
XThis makefile contains quite a bit of repetitive information.  Make
Xmacros can be used to reduce this repetition.  MAKE translates two kinds of
Xmacros.  ^*Named macros\* allow arbitrary strings of text to be placed
Xwherever they are needed.  ^*Dependency macros\* provide a notation to
Xrefer to the parts of a dependency statement.
X Three dependency macros are automatically defined within each
Xdependency statement.  Each is identified by a single punctuation
Xcharacter.  The name of the
Xcurrent target is given by the dependency macro '@'.  The name of the
Xcurrent target with its file type (e.g., .for, .obj, etc.) deleted
Xis given by the dependency macro '_*'.  The list of prerequisites
Xof the current target is given by '?'.  Each dependency macro is invoked
Xby a '$' followed by the single character name of the macro.
X The first dependency statement in the above makefile is,
X.nf
X.b 1
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=lab5.exe first.obj, second.obj, third.obj
X#-----
X.b 1
X.f
XThe target of this statement is lab5.exe.  Throughout the rest of the
Xstatement, the dependency macro '@' is defined as 'lab5.exe', and the
Xdependency macro '_*' is defined as just 'lab5' (because the .exe is
Xdeleted for the '_*' macro).  The dependency macro '@' can be
Xused to rewrite the command line of this statement:
X.b 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ first.obj, second.obj, third.obj
X#-----
X.f
X.b 1
XWhen MAKE processes the command line, it will replace the '$@' with
X'lab5.exe'.  Similarly, the other dependency statements in the makefile
Xcan be re-written using the '_*' macro to produce,
X.b 1
X.nf
X#-----
X    # This makefile is exactly equivalent to the
X    # first makefile given for this example.
X
X# Within this statement, '$@' is defined to be 'lab5.exe'.
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ first.obj, second.obj, third.obj
X
X# Within this statement, '$_*' is defined to be 'first'.
X# So MAKE translates '$_*.for' into 'first.for'.
Xfirst.obj: $_*.for
X        fortran $_*.for
X
X# Within this statement, '$_*' is defined to be 'second'.
X# So MAKE translates '$_*.for' into 'second.for'.
Xsecond.obj: $_*.for
X        fortran $_*.for
X
X# Within this statement, '$_*' is defined to be 'third'.
X# So MAKE translates '$_*.for' into 'third.for'.
Xthird.obj: $_*.for
X        fortran $_*.for
X#-----
X.f
X Of course, $_* and $@ ^*cannot be used in the target part of a
Xdependency statement\*, since the target part of the statement
Xis what ^*defines\* these macros.  For example, both dependency
Xstatements in the following makefile are invalid:
X.b 1
X.nf
X#-----
X$_*.obj: foo1.for
X        fortran foo1.for
X$@: foo2.for
X        fortran foo2.for
X#-----
X.f
X The example makefile can be further simplified using the '?' macro.
XAs stated above, this macro is defined as the list of prerequisites
Xof the dependency statement it is used in.  The example makefile, with
X$? used in all dependencies except the first, is,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ first.obj, second.obj, third.obj
Xfirst.obj: $_*.for
X        fortran $?
Xsecond.obj: $_*.for
X        fortran $?
Xthird.obj: $_*.for
X        fortran $?
X#-----
X.f
X The '?' macro is a bit harder to use in the first dependency statement,
Xbecause the arguments to link must be separated by commas.  If we just
Xreplace the link arguments with '$?', we get,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ $?
X#-----
X.f
X.bl 1
XMAKE will translate this dependency statement into,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ first.obj second.obj third.obj
X#-----
X.f
X.bl 1
XThis link command line will fail, because the arguments are not
Xseparated by commas.  The next idea is to append a comma to the
Xuse of the '?' macro, just as '.for' was appended to the use of
Xthe '_*' macro.  That is, change the dependency statement to,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ $?,
X#-----
X.f
X.bl 1
XMAKE will expand this as,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ first.obj, second.obj, third.obj,
X#-----
X.f
X.bl 1
XThis is almost right -- notice that the comma was appended to ^*each\*
Xname in the prerequisite list.  Unfortunately, the comma appended to
Xthe last filename will cause another link error.  To get around this,
Xif any single character is placed ^*between\* the '$' and the macro symbol
X(in this case '?'), that character will be used as a ^*separator\* when
XMAKE expands the macro.  The most common separators are ',' and '+'.
XUsing this feature, we can re-write the makefile as,
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ $,?
Xfirst.obj: $_*.for
X        fortran $?
Xsecond.obj: $_*.for
X        fortran $?
Xthird.obj: $_*.for
X        fortran $?
X#-----
X.f
X.bl 1
XMAKE will expand this makefile into precisely the original makefile.
X This makefile uses all three dependency macros; '_*', '@', and '?'.
XNonetheless, it still has a noticeable amount of repetition.  The
Xlast three dependency statements are identical, except for the names of
Xtheir targets.  As mentioned briefly above, MAKE allows a dependency
Xstatement to have more than one name in its target list.  When MAKE finds
Xsuch a statement in a makefile, it creates a copy of the statement
Xfor each name in the target list.  Each copy has only one
Xname in its target list, but is otherwise identical to the original
Xstatement -- each has the same dependency list and command lines.  We can
Xtake advantage of this to compress the example makefile still further:
X.bl 1
X.nf
X#-----
Xlab5.exe: first.obj second.obj third.obj
X        link /exe=$@ $,?
Xfirst.obj second.obj third.obj: $_*.for
X        fortran $?
X#-----
X.f
X.bl 1
XMAKE will expand the second dependency statement into three separate
Xstatements, each with one target.  It will then expand the macros in
Xeach target to produce the original example makefile.
X MAKE also allows user-defined macros called ^*named macros\*.  A
Xnamed macro is defined by a line containing the macro name, followed
Xby an equal sign ('='), followed by the text for the definition.  Once
Xa named macro has been defined, it can be used like a dependency macro.
XInstead of a single punctuation character, a named macro is referred
Xto by its name, in parentheses, after the '$'.  We can reduce the
Xexample makefile further by defining a named macro for the various
Xmodules of the lab5.exe program:
X.bl 1
X.nf
X#-----
XLABMODULES = first.obj second.obj third.obj
X
Xlab5.exe: $(LABMODULES)
X        link /exe=$@ $,?
X$(LABMODULES): $_*.for
X        fortran $?
X#-----
X.f
X.bl 1
XThis makefile can be easily updated if a new module (say, fourth.obj)
Xis added to lab5.exe.  Only the definition of LABMODULES must change.
XEverything else will be taken care of automatically by the macro and
Xtarget expansion which MAKE performs.
X MAKE expands each use of a named macro in the same way it expands
Xeach use of a dependency macro.  Text attached to the use of the macro
X(e.g., the '.for' in '$_*.for') is attached to each name in the macro
Xdefinition after the expansion.  Also, a separator character can be inserted
Xafter the '$' and before the '('.  For example, the following makefile
Xis exactly equivalent to the one just given:
X.bl 1
X.nf
X#-----
XLABMODULES = first second third
X
Xlab5.exe: $(LABMODULES).obj
X        link /exe=$@ $,(LABMODULES).obj
X$(LABMODULES).obj: $_*.for
X        fortran $?
X#-----
X.f
X.bl 1
XThe trickiest part of this makefile is the use of LABMODULES in the
Xlink command line, '$,(LABMODULES).obj'.  MAKE expands this macro
Xin two steps.  First, it creates the list of names in the expansion by
Xcopying the attached text ('.obj') after each name in the definition
Xof LABMODULES.  Then it appends the separator character ',' to every
Xname except the last in the expansion list.
X.bl 1
X.c
X^*Default Rules\*
X In the example makefile above, the second dependency statement tells
XMAKE how to create each object file from its fortran source file.
XThis is such a simple thing that it is inconvenient to have to specify
Xit at all -- if we need to create an object file from ^*any\* fortran
Xfile, we must use the fortran compiler to do it.  MAKE can be told
Xthis by a ^*default rule\*.  A default rule tells MAKE how to
Xcreate one particular kind of file from another kind.  In this case,
Xwe need a rule which specifies how to create a .obj file from a .for
Xfile.  The default rule for this is,
X.bl 1
X.nf
X#-----
X_.for.obj:
X        fortran $_*.for
X#-----
X.f
X The default rule statement has a syntax similar to a dependency statement.
XIts "target" is just the source file type followed by the target
Xfile type.  Unlike a dependency statement, every default rule
Xstatement must include at least one command line -- otherwise, the
Xdefault rule is useless.
XOrdinarily, no prerequisites are named in the default rule.
XMAKE knows that the actual target (in this case, the .obj file)
Xdepends on the source (.for) file, so it automatically makes the
Xsource file a prerequisite of the target file.  Since MAKE can infer
Xwhat the target and prerequisite files are, all of the dependency macros
Xare defined within each default rule statement.
X In the example above, for instance, '$_*.for' is ^*not\* expanded into
X'.for.for', as one might expect (since the target is '.for.obj', and
Xtherefore the target without its .obj file type is '.for').  Instead,
Xwhen MAKE needs to update an object file which is not a target of any
Xdependency statement, and a fortran file with the same name exists,
XMAKE will automatically create a dependency statement whose target is
Xthe object file, and whose only prerequisite is the fortran file.  The
Xcommand line of the default rule, 'fortran $_*.for' will become the only
Xcommand line of this new dependency.  MAKE will then use the dependency
Xstatement just like any other to update the object file.
X Other prerequisites of a default rule
Xcan be specified after the ':'.  For example, if every fortran source
Xfile includes a file called stuff.inc, the following default rule
Xcan be used.
X.bl 1
X.nf
X#-----
X_.for.obj: stuff.inc
X        fortran $_*.for
X#-----
X.f
X A number of commonly used default rules are provided in the
Xdefault rules file.  MAKE reads this file before reading the
Xmakefile.  The default rules file name is given in the logical
Xname MAKE$DEFRULE.  If a rule is defined in the default rules
Xfile, and re-defined in the makefile, the makefile definition
Xwill be used.  If a rule is defined several times, only the last
Xdefinition which MAKE reads will be used.
X.bl 1
X.c
X^*Command Lines\*
X Every MAKE statement can contain one or more DCL ^*command lines\*
Xwhich are executed, in the order they are given,
Xwhen the target of the statement is out of date with respect to any of
Xits prerequisites.  Command lines from the makefile are executed in
Xa single sub-process which MAKE creates with
Xthe same "context" as the user's process.  This means that any symbols
Xor logical names which are defined in the user's process are also defined
Xin the MAKE sub-process, and thus can be used in command lines inside the
Xmakefile.
X When a command line is executed, MAKE checks to see if the command exits
Xwith a status value indicating error.  If a command signals an error, MAKE
Xaborts, since the likeliest interpretation is that the current target cannot
Xbe updated.  If the command line is preceded by '-', MAKE will ignore the exit
Xstatus, and continue executing even if an error occurs.  (The MAKE command
Xoption /IGNORE causes MAKE to ignore all command execution errors.)  The
Xfollowing makefile demonstrates the use of '-'.
X.bl 1
X.nf
X#-----
Xtest.exe: test.obj
X        - del $@;_*
X        link $?
X#-----
X.f
X.bl 1
XThe '-' is necessary because if the target file TEST.EXE does not exist,
Xthe delete command will return an error status.  MAKE should ignore
Xthis error, if it occurs, because it is unimportant.
X By default, MAKE prints each command line as it is executed.
XIf the command line is preceded by '@', it will not be
Xprinted when executed.  The MAKE command option /SILENT suppresses
Xprinting of all command lines.  The following makefile uses '@'
Xto print a message without displaying the command line responsible.
X.bl 1
X.nf
X#-----
Xtest.exe: test.obj io.obj
X        link $,?
X        @ write sys\$output "TEST.EXE created, deleting objects."
X        - del $,?;_*
X#-----
X.f
X.bl 1
X.c
X^*Literal Characters\*
X Notice the use of backslash in the makefile just given.  This is the
X"accept" character for make, as '__' is the accept character for RUNOFF.
XIt tells MAKE that the character which follows it is not to have its usual
Xsyntactic meaning, but should instead be treated like any ordinary character.
XIn this case, it tells MAKE that the '$' in 'sys$output' does not indicate
Xuse of a macro, but is just another character in the command line.
X Another use of backslash is suggested by the same command line.  The initial
X'@' tells MAKE not to print the command line when it is executed.  This
Xis also the character which tells DCL to run a command file.  To get
Xthis second meaning, the '@' must be preceded by a backslash, which tells
XMAKE that the '@' is part of the command line.
X One further use of backslash is to continue long lines in makefiles.  If a
Xbackslash is found at the end of a line, MAKE deletes it and interprets the
Xend of line as a blank.  This is demonstrated in the following makefile.
X.bl 1
X.nf
X#-----
Xtest.exe: file1.obj file2.obj file3.obj file4.obj file5.obj \
X    file6.obj file7.obj file8.obj file9.obj file10.obj
X        link /exe=$@ $,?
X#-----
X.f
$ GoSub Convert_File
$ File_is="MAKEFILE.MAK"
$ Check_Sum_is=1997041993
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
XCC_QUAL =
XLINK_QUAL =
X
X# A macro to print messages for user.
X
XSAY = @write sys\$output
X
X# What to make by default.
X
Xstuff: make.hlb make.mem make.exe
X`009$(SAY) "********** Everything is up to date **********"
X`009purge
X
X# Modules needed to create make.exe
X
XMK_MOD = mkdate mkexit mkext mkfile mkmacro mkmain \
X`009mkscan mkshow mksub mkutil
X
X# How to create the make program.
X
Xmake.exe: $(MK_MOD).obj mkerrs.obj
X`009$(SAY) "********** CREATING NEW VERSION **********"
X`009-del $@;*
X`009link $(LINK_QUAL) /exe=$* $?, c_opts/o
X$(MK_MOD).obj: make.h
X
X# How to create the stand alone test programs.
X
Xext: exitast.exe
Xsub: subproc.exe
Xexitast.exe subproc.exe: $*.obj
X`009link $*, c_opts/o
X`009del $?;*
Xexitast.obj: mkexit.c
X`009cc /def=AST_STAND_ALONE /obj=$* $?
Xsubproc.obj: mksub.c
X`009cc /def=SUB_STAND_ALONE /obj=$* $?
X
X# How to make the help library.
X
Xmake.hlb: $*.hlp
X`009lib/cre/help $* $*
X
X# Rule to create the help file.
X
X.rnh.hlp:
X`009runo $?
$ GoSub Convert_File
$ File_is="MAKE_STARTUP.COM"
$ Check_Sum_is=187269125
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X$!
X$! This is the startup file needed to for MAKE.
X$!
X$! This line must be modified to point to where you decide to place
X$! MAKE and associated files.
X$!
X$ define/system/exec make_homedir  DISK:[DIR]
X$! 
X$ define/system make$defrule make_homedir:defrule.mak
X$ define/system make$document make_homedir:make.mem
X$!
X$! Define the default file name for a Makefile. (Users can override
X$! this by defining make$defname themselves. If undefined then
X$! make will look for MAKE$DEFNAME.MAK in the current working dir.)
X$!
X$ define/system make$defname makefile.mak  
X$!
$ GoSub Convert_File
$ File_is="MKDATE.C"
$ Check_Sum_is=1359303793
$ 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
Xmkdate.c
XThis file contains the routines which deal with file modification
Xdates, including the date caching routines.
X*/
X
X#include <fab.h>
X#include <xab.h>
X#include "make.h"
X
Xtypedef struct cache_entry {
X`009struct cache_entry *ce_next;
X`009DATE ce_filedate;
X`009char ce_filename[1];`009/* will actually be longer than one */
X} CACHE_ENTRY;
X
Xstatic CACHE_ENTRY *Cacheptr;
Xstatic int Cache_n_cmd;
X
X/* Determine the modify date of the named file and put the date in the
Xbuffer pointed to by fdateptr.  Return 0 for success, -1 if the date
Xcannot be obtained. */
X
Xget_file_date(fname, fdateptr)
Xchar *fname;
XDATE *fdateptr;
X{
X`009int dstat;
X`009int fnmdsc[2];
X
X`009/* See if file is in file-date cache. */
X
X`009if (Cache_n_cmd != Num_cmd_sent) {
X`009`009clear_cache();
X`009`009Cache_n_cmd = Num_cmd_sent;
X`009} else if (get_cache_date(fname, fdateptr) == 0) {
X`009`009return (0);
X`009}
X
X`009/* Since file not cached, we have to find it out. */
X
X`009fnmdsc[0] = strlen(fnmdsc[1] = (int)fname);
X`009if (Created) {
X`009`009dstat = getfdate(fnmdsc, fdateptr, NULL, NULL, NULL);
X`009} else {
X`009`009dstat = getfdate(fnmdsc, NULL, fdateptr, NULL, NULL);
X`009}
X
X`009/* If the date was found, add the file-date to the cache. */
X
X`009if (ERRSTAT(dstat)) {
X`009`009return (-1);
X`009} else {
X`009`009add_cache_date(fname, fdateptr);
X`009}
X`009return (0);
X}
X
X/* See if a file-date is in the cache. */
X
Xstatic
Xget_cache_date(fname, fdateptr)
Xchar *fname;
XDATE *fdateptr;
X{
X`009register CACHE_ENTRY *cptr;
X
X`009for (cptr = Cacheptr; cptr != NULL; cptr = cptr->ce_next) {
X`009`009if (COMPARE(fname, cptr->ce_filename) == 0) {
X`009`009`009if (fdateptr != NULL) {
X`009`009`009`009fdateptr->date_lo = cptr->ce_filedate.date_lo;
X`009`009`009`009fdateptr->date_hi = cptr->ce_filedate.date_hi;
X`009`009`009}
X#ifdef CACHE_DEBUG
Xprintf("**** %s date found in cache.\n", fname);
X#endif
X`009`009`009return (0);
X`009`009}
X`009}
X`009return (-1);
X}
X
X/* Add a new file-date to the cache. */
X
Xstatic
Xadd_cache_date(fname, fdateptr)
Xchar *fname;
XDATE *fdateptr;
X{
X`009register CACHE_ENTRY *cptr;
X
X`009cptr = (CACHE_ENTRY *)err_alloc(sizeof(CACHE_ENTRY) + strlen(fname));
X`009strecpy(fname, cptr->ce_filename);
X`009cptr->ce_filedate.date_lo = fdateptr->date_lo;
X`009cptr->ce_filedate.date_hi = fdateptr->date_hi;
X
X`009cptr->ce_next = Cacheptr;
X`009Cacheptr = cptr;
X
X#ifdef CACHE_DEBUG
Xprintf("**** %s date added to cache.\n", fname);
X#endif
X`009return (0);
X}
X
X/* Empty the cache -- a command has been executed, so any
Xor all of the cached file-dates may be wrong. */
X
Xstatic
Xclear_cache()
X{
X`009register CACHE_ENTRY *cptr, *nextptr;
X
X`009for (cptr = Cacheptr; cptr != NULL; cptr = nextptr) {
X`009`009nextptr = cptr->ce_next;
X`009`009free(cptr);
X`009}
X`009Cacheptr = NULL;
X#ifdef CACHE_DEBUG
Xprintf("**** Cache cleared.\n");
X#endif
X}
X
X/* Set the modification date of a file to the current time. */
X
Xtouch_file(filename)
Xchar *filename;
X{
X`009int fnmdsc[2], currtime[2];
X
X`009if (Verbose) {
X`009`009printf("\r\nTouching %s\r\n", filename);
X`009}
X`009fnmdsc[0] = strlen(fnmdsc[1] = (int)filename);
X`009currtime[0] = currtime[1] = 0;`009/* setfdate will fill in current time */
X`009if (Created) {
X`009`009setfdate(fnmdsc, currtime, NULL, NULL, NULL);
X`009} else {
X`009`009setfdate(fnmdsc, NULL, currtime, NULL, NULL);
X`009}
X}
X
X/* Return TRUE if the date pointed to by depdptr is later than the date
Xpointed to by targdptr. */
X
Xout_of_date(depdptr, targdptr)
XDATE *depdptr, *targdptr;
X{
X`009return (depdptr->date_hi > targdptr->date_hi ||
X`009    (depdptr->date_hi == targdptr->date_hi &&
X`009    depdptr->date_lo > targdptr->date_lo));
X}
X
X/* Print the ascii version of a file date. */
X
Xprintdate(bindate)
Xregister DATE *bindate;
X{
X`009STR_DESC date_desc;
X`009char datebuf[ASCII_DATE_LEN];
X
X`009if (bindate->date_lo == -1 && bindate->date_hi == -1) {
X`009`009printf("<DATE UNAVAILABLE>");
X`009`009return (-1);
X`009}
X
X`009date_desc.sd_len = ASCII_DATE_LEN;
X`009date_desc.sd_str = datebuf;
X`009datebuf[ASCII_DATE_LEN] = '\0';
X`009sys$asctim(NULL, &date_desc, bindate);
X
X`009printf(datebuf);
X`009return (0);
X}
$ GoSub Convert_File
$ Goto Part4