$! ------------------ CUT HERE -----------------------
$ v='f$verify(f$trnlnm("SHARE_VERIFY"))'
$!
$! This archive created by VMS_SHARE Version 7.2-007  22-FEB-1990
$!   On 13-JUN-1990 21:10:37.96   By user MIGLESIAS 
$!
$! This VMS_SHARE Written by:
$!    Andy Harper, Kings College London UK
$!
$! Acknowledgements to:
$!    James Gray       - Original VMS_SHARE
$!    Michael Bednarek - Original Concept and implementation
$!
$!+ THIS PACKAGE DISTRIBUTED IN 6 PARTS, TO KEEP EACH PART
$!  BELOW 30 BLOCKS
$!
$! TO UNPACK THIS SHARE FILE, CONCATENATE ALL PARTS IN ORDER
$! AND EXECUTE AS A COMMAND PROCEDURE  (  @name  )
$!
$! THE FOLLOWING FILE(S) WILL BE CREATED AFTER UNPACKING:
$!       1. CLIDEF.H;1
$!       2. CMDLINE.C;1
$!       3. CMDLINE.H;1
$!       4. DATES.C;1
$!       5. FUNCTIONS.H;1
$!       6. MAKE.C;11
$!       7. MAKE.H;1
$!       8. MAKE.HLP;23
$!       9. MAKE.INI;17
$!      10. MAKE.OPT;5
$!      11. MAKECMD.CLD;19
$!      12. MAKEFILE.;5
$!      13. MSTRING.C;1
$!      14. RELEASE.V34;1
$!      15. SIMPLE.H;1
$!      16. STRINGS.H;1
$!
$set="set"
$set symbol/scope=(nolocal,noglobal)
$f=f$parse("SHARE_TEMP","SYS$SCRATCH:.TMP_"+f$getjpi("","PID"))
$e="write sys$error  ""%UNPACK"", "
$w="write sys$output ""%UNPACK"", "
$ if f$trnlnm("SHARE_LOG") then $ w = "!"
$ ve=f$getsyi("version")
$ if ve-f$extract(0,1,ve) .ges. "4.4" then $ goto START
$ e "-E-OLDVER, Must run at least VMS 4.4"
$ v=f$verify(v)
$ exit 44
$UNPACK: SUBROUTINE ! P1=filename, P2=checksum
$ if f$search(P1) .eqs. "" then $ goto file_absent
$ e "-W-EXISTS, File ''P1' exists. Skipped."
$ delete 'f'*
$ exit
$file_absent:
$ if f$parse(P1) .nes. "" then $ goto dirok
$ dn=f$parse(P1,,,"DIRECTORY")
$ w "-I-CREDIR, Creating directory ''dn'."
$ create/dir 'dn'
$ if $status then $ goto dirok
$ e "-E-CREDIRFAIL, Unable to create ''dn'. File skipped."
$ delete 'f'*
$ exit
$dirok:
$ w "-I-PROCESS, Processing file ''P1'."
$ if .not. f$verify() then $ define/user sys$output nl:
$ EDIT/TPU/NOSEC/NODIS/COM=SYS$INPUT 'f'/OUT='P1'
PROCEDURE Unpacker ON_ERROR ENDON_ERROR;SET(FACILITY_NAME,"UNPACK");SET(
SUCCESS,OFF);SET(INFORMATIONAL,OFF);f:=GET_INFO(COMMAND_LINE,"file_name");b:=
CREATE_BUFFER(f,f);p:=SPAN(" ")@r&LINE_END;POSITION(BEGINNING_OF(b));
LOOP EXITIF SEARCH(p,FORWARD)=0;POSITION(r);ERASE(r);ENDLOOP;POSITION(
BEGINNING_OF(b));g:=0;LOOP EXITIF MARK(NONE)=END_OF(b);x:=ERASE_CHARACTER(1);
IF g=0 THEN IF x="X" THEN MOVE_VERTICAL(1);ENDIF;IF x="V" THEN APPEND_LINE;
MOVE_HORIZONTAL(-CURRENT_OFFSET);MOVE_VERTICAL(1);ENDIF;IF x="+" THEN g:=1;
ERASE_LINE;ENDIF;ELSE IF x="-" THEN IF INDEX(CURRENT_LINE,"+-+-+-+-+-+-+-+")=
1 THEN g:=0;ENDIF;ENDIF;ERASE_LINE;ENDIF;ENDLOOP;t:="0123456789ABCDEF";
POSITION(BEGINNING_OF(b));LOOP r:=SEARCH("`",FORWARD);EXITIF r=0;POSITION(r);
ERASE(r);x1:=INDEX(t,ERASE_CHARACTER(1))-1;x2:=INDEX(t,ERASE_CHARACTER(1))-1;
COPY_TEXT(ASCII(16*x1+x2));ENDLOOP;WRITE_FILE(b,GET_INFO(COMMAND_LINE,
"output_file"));ENDPROCEDURE;Unpacker;QUIT;
$ delete/nolog 'f'*
$ CHECKSUM 'P1'
$ IF CHECKSUM$CHECKSUM .eqs. P2 THEN $ EXIT
$ e "-E-CHKSMFAIL, Checksum of ''P1' failed."
$ ENDSUBROUTINE
$START:
$ create 'f'
X#define cli$m_debug %x00000001
X#define cli$m_dbgtru %x00000002
X#define cli$m_verify %x00000004
X#define cli$m_batch %x00000008
X#define cli$m_indirect %x00000010
X#define cli$m_vfyinp %x00000020
X#define cli$m_trmvrblv %x00000300
X#define cli$m_dbgexcp %x00010000
X#define cli$m_parmreq %x00000001
X#define cli$m_absadr %x00000002
X#define cli$m_expnam %x00000004
X#define cli$m_lastval %x00000001
X#define cli$m_dummy %x00000002
X#define cli$m_parmprs %x00000001
X#define cli$m_concatinp %x00000002
X#define cli$m_moreinp %x00000004
X#define cli$m_parmdef %x00000008
X#define cli$m_morevals %x00000001
X#define cli$m_keyvalu %x00000002
X#define cli$k_reqdesc %x0000001c`09
X#define cli$c_reqdesc %x0000001c`09
X
X#define cli$m_nowait %x00000001
X#define cli$m_noclisym %x00000002
X#define cli$m_nolognam %x00000004
X#define cli$m_nokeypad %x00000008
X#define cli$m_notify %x00000010
X#define cli$m_nocontrol %x00000020
X#define cli$k_spawn_version %x00000001`20
X#define cli$c_spawn_version %x00000001`20
X#define cli$k_srvdesc %x00000054`09
X#define cli$c_srvdesc %x00000054`09
X
X#define cli$m_alloccur %x00000001
X#define cli$m_qdusrv %x00000002
X#define cli$m_qdexpa %x00000004
X#define cli$m_qualtru %x00000001
X#define cli$m_qualexp %x00000002
X#define cli$k_qualdef %x00000014`09
X#define cli$c_qualdef %x00000014`09
X#define cli$k_qdbits %x00000014`09
X#define cli$c_qdbits %x00000014`09
X#define cli$k_workarea %x00000080`09
X#define cli$c_workarea %x00000080`09
X
X#define cli$k_utilopr %x00000000`09
X#define cli$k_inpspec %x00000001`09
X#define cli$k_outspec %x00000002`09
X#define cli$k_pardone %x00000003`09
X#define cli$k_valconv %x00000004`09
X#define cli$k_clint %x00000005`09
X
X#define cli$k_initprs %x00000000`09
X#define cli$k_getcmd %x00000001`09
X#define cli$k_getqual %x00000002`09
X#define cli$k_getopt %x00000003`09
X#define cli$k_getline %x00000004`09
X
X
X
X
X
X
X
X#define cli$k_cliserv %x00000005`09
X
X#define cli$k_input1 %x00000010`09
X#define cli$k_input2 %x00000011`09
X#define cli$k_input3 %x00000012`09
X#define cli$k_input4 %x00000013`09
X
X#define cli$k_output1 %x00000020`09
X#define cli$k_output2 %x00000021`09
X#define cli$k_output3 %x00000022`09
X#define cli$k_output4 %x00000023`09
X
X#define cli$k_endprm1 %x00000030`09
X#define cli$k_endprm2 %x00000031`09
X#define cli$k_endprm3 %x00000032`09
X#define cli$k_endprm4 %x00000033`09
X
X#define cli$k_numerval %x00000040`09
X#define cli$k_asciival %x00000041`09
X#define cli$k_keyword %x00000042`09
X#define cli$k_keyval %x00000043`09
X#define cli$k_filspec %x00000044`09
X
X#define cli$k_present %x00000050`09
X#define cli$k_getvalue %x00000051`09
X#define cli$k_endparse %x00000052`09
X#define cli$k_dclparse %x00000053`09
X#define cli$k_dispatch %x00000054`09
X#define cli$k_nextqual %x00000055`09
X`20
$ CALL UNPACK CLIDEF.H;1 191254349
$ create 'f'
X#include <stdio.h>
X#include <descrip.h>
X#include <ssdef.h>
X#include <climsgdef.h>
X#include "strings.h"
X#include "cmdline.h"
X
Xglobalref int makecmdtable;
X
X/************************************************************/
Xprocess_cmdline(char *targname`5B`5D)
X`7B
X  int i;
X  int targcnt;
X  int status;
X  varstr(256) tmpstr,cmdstr;
X  vardesc tmpdsc,cmddsc;
X  strdesc precmd;
X  int handler();
X  char *tlist,*ptr,*tmp;
X
X  VAXC$ESTABLISH(handler);
X
X  DESCRIP(precmd,"MAKE ");
X  VDESCRIP(tmpdsc,tmpstr);
X  VDESCRIP(cmddsc,cmdstr);
X  status = lib$get_foreign(&tmpdsc);
X  str$concat(&cmddsc,&precmd,&tmpdsc);
X  status = cli$dcl_parse(&cmddsc,&makecmdtable,0,0,0);
X  for(i=0; i<MAXKEYS; i++)
X    (*keylist`5Bi`5D.readkey)(keylist`5Bi`5D.name,keylist`5Bi`5D.var);
X  targcnt = 0;
X  for(i=1; i<9; i++) `7B
X    if(!p`5Bi`5D) continue;`09`09/* Skip empty boxes */
X    if(*p`5Bi`5D == '\0') continue;`09/* Skip empty boxes */
X    targname`5Btargcnt`5D = strperm(p`5Bi`5D);
X    DOWNCASE(targname`5Btargcnt`5D,tmp);
X    targcnt++;
X    free(p`5Bi`5D);
X  `7D
X  return(targcnt);
X`7D
X/************************************************************/
Xread_key_boolean(char *keyname,int *keyvar)
X`7B
X  strdesc entity;
X
X  DESCRIP(entity,keyname);
X  switch(cli$present(&entity)) `7B
X  case CLI$_PRESENT:
X  case CLI$_DEFAULTED:
X  case CLI$_LOCPRES:
X    *keyvar = TRUE;
X    break;
X  case CLI$_NEGATED:
X  case CLI$_LOCNEG:
X    *keyvar = FALSE;
X    break;
X  case CLI$_ABSENT:
X    *keyvar = FALSE;
X    break;
X  default:
X    error("Unknown CLI$Present() return status.");
X    break;
X  `7D
X`7D
X/************************************************************/
Xread_key_text(char *keyname,char **keyvar)
X`7B
X  strdesc entity;
X  short keylen;
X  strdesc keyval;
X  char keybuf`5B255`5D;
X
X  DESCRIP(entity,keyname);
X  keybuf`5B0`5D = '\0';
X  DESCRIP(keyval,keybuf);
X  keyval.dsc$w_length = 255;
X  switch(cli$present(&entity)) `7B
X  case CLI$_PRESENT:
X  case CLI$_DEFAULTED:
X  case CLI$_LOCPRES:
X    cli$get_value(&entity,&keyval,&keylen);
X    if(!(*keyvar = malloc(keylen+1))) `7B
X      sprintf(keybuf,"Unable to allocate memory for parameter %s, length %d.
V",
X`09      entity.dsc$a_pointer,keylen);
X      error(keybuf);
X    `7D
X    strncpy(*keyvar,keyval.dsc$a_pointer,keylen);
X    (*keyvar)`5Bkeylen`5D = '\0';
X    break;
X  case CLI$_ABSENT:
X    *keyvar = NULL;
X    break;
X  default:
X    error("Unknown CLI$Present() return status.");
X    break;
X  `7D
X`7D
X/************************************************************/
Xhandler(sigargs,mechargs)
X     unsigned long sigargs`5B`5D;
X     unsigned long mechargs`5B`5D;
X`7B
X  globalvalue CLI$_FACILITY;
X  char buf1`5B80`5D;
X  char buf2`5B80`5D;
X  strdesc desc1;
X  strdesc desc2;
X  unsigned char otherargs`5B4`5D;
X  unsigned short str_len;
X
X  DESCRIP(desc1,buf1);
X  DESCRIP(desc2,buf2);
X  desc1.dsc$w_length = 80;
X  desc2.dsc$w_length = 80;
X
X  if ((sigargs`5B1`5D >> 16 & 0XFFF) == CLI$_FACILITY) `7B
X    exit(sigargs`5B1`5D);
X  `7D
X  else
X    return(SS$_RESIGNAL);
X`7D
$ CALL UNPACK CMDLINE.C;1 1779784161
$ create 'f'
Xint read_key_boolean();
Xint read_key_text();
X
Xstruct key `7B
X  char *name;
X  int *var;
X  int (*readkey)();
X`7D;
X
Xint spawn,verify,ignore_errors,execute,
X`09`09debug,keep,usedefaults,alwaysrebuild,usage_flag;
Xchar *makefilename,*cmd_fname,*targetnamelist;
Xchar *p`5B9`5D;`09`09`09/* Only 8, but starting at 1 :-) */
X
X#define BOOLEAN_KEY(KEYWORD,KEYVAR) \
X`09`09`7BKEYWORD,KEYVAR,read_key_boolean`7D
X#define TEXT_KEY(KEYWORD,KEYVAR) \
X`09`09`7BKEYWORD,KEYVAR,read_key_text`7D
X
X#define MAXKEYS 19
Xstruct key keylist`5BMAXKEYS`5D = `7B
X  BOOLEAN_KEY("SPAWN",&spawn),
X  BOOLEAN_KEY("VERIFY",&verify),
X  BOOLEAN_KEY("IGNORE",&ignore_errors),
X  BOOLEAN_KEY("EXECUTE",&execute),
X  BOOLEAN_KEY("DEBUG",&debug),
X  BOOLEAN_KEY("KEEP",&keep),
X  BOOLEAN_KEY("DEFAULTS",&usedefaults),
X  BOOLEAN_KEY("FORCE",&alwaysrebuild),
X  BOOLEAN_KEY("HELP",&usage_flag),
X  TEXT_KEY("INPUT",&makefilename),
X  TEXT_KEY("OUTPUT",&cmd_fname),
X  TEXT_KEY("P1",&p`5B1`5D),
X  TEXT_KEY("P2",&p`5B2`5D),
X  TEXT_KEY("P3",&p`5B3`5D),
X  TEXT_KEY("P4",&p`5B4`5D),
X  TEXT_KEY("P5",&p`5B5`5D),
X  TEXT_KEY("P6",&p`5B6`5D),
X  TEXT_KEY("P7",&p`5B7`5D),
X  TEXT_KEY("P8",&p`5B8`5D),
X`7D;
$ CALL UNPACK CMDLINE.H;1 456416498
$ create 'f'
X#include <descrip.h>
X#include "strings.h"
X#include "simple.h"
X#include "cmdline.h"
X
Xstatic int CMS$_NORMAL;
X
X/************************************************************/
Xreadcmsdate(char *library,char *generation,char *module,int date`5B2`5D)
X`7B
X  static int (*cms_setlib)();
X  static int (*cms_showgen)();
X  static int process_cms_output();
X  static int process_cms_messages();
X  static char last_lib`5B100`5D;
X  static int lib_data_block`5B50`5D;
X  static int initialized = FALSE;
X  strdesc libdesc,moddesc,gendesc,*gendscptr;
X  strdesc cmsimage,symnam;
X  int status;
X
X  if(!initialized) `7B
X    Dprintf("Initializing CMS interface.\n");
X    DESCRIP(cmsimage,"CMSSHR");
X    DESCRIP(symnam,"CMS$SET_LIBRARY");
X    status = lib$find_image_symbol(&cmsimage,&symnam,&cms_setlib);
X    if(!(status & 1))
X      error("Error initializing CMS interface.");
X    DESCRIP(symnam,"CMS$SHOW_GENERATION");
X    status = lib$find_image_symbol(&cmsimage,&symnam,&cms_showgen);
X    if(!(status & 1))
X      error("Error initializing CMS interface.");
X    DESCRIP(symnam,"CMS$_NORMAL");
X    status = lib$find_image_symbol(&cmsimage,&symnam,&CMS$_NORMAL);
X    if(!(status & 1))
X      error("Error initializing CMS interface.");
X    initialized = TRUE;
X    last_lib`5B0`5D = '\0';
X  `7D
X  if(strcmp(last_lib,library) != 0) `7B
X    DESCRIP(libdesc,library);
X    strcpy(last_lib,library);
X    (*cms_setlib)(lib_data_block,&libdesc,0);
X  `7D
X  if(generation) `7B
X    DESCRIP(gendesc,generation);
X    gendscptr = &gendesc;
X  `7D
X  else `7B
X    gendscptr = 0;
X  `7D
X
X  DESCRIP(moddesc,module);
X  status =
X    (*cms_showgen)(lib_data_block, /* library_data_block */
X`09`09   process_cms_output, /* output_routine */
X`09`09   date,`09/* user_arg */
X`09`09   &moddesc,`09/* element_expression */
X`09`09   gendscptr,`09/* generation_expression */
X`09`09   0,`09`09/* from_generation_expression */
X`09`09   0,`09`09/* ancestors */
X`09`09   0,`09`09/* descendants */
X`09`09   0,`09`09/* member_list */
X`09`09   process_cms_messages); /* msg_routine */
X
X  if(status != CMS$_NORMAL)
X    return(FALSE);
X
X  return(TRUE);
X`7D
X/************************************************************/
Xprocess_cms_output(int *new_element,
X`09`09   int *ldb,
X`09`09   int *user_param,
X`09`09   int *element_id,
X`09`09   int *generation_id,
X`09`09   int *user_name_id,
X`09`09   int *trans_time,
X`09`09   int *create_time,
X`09`09   int *revision_time,
X`09`09   int *remark_id,
X`09`09   int *class_list_id,
X`09`09   int *format,
X`09`09   int *attributes,
X`09`09   int *revision_number,
X`09`09   int *reservations)
X`7B
X  user_param`5B0`5D = revision_time`5B0`5D;
X  user_param`5B1`5D = revision_time`5B1`5D;
X  return(CMS$_NORMAL);
X`7D
X
X/************************************************************/
Xprocess_cms_messages()
X`7B
X  return(CMS$_NORMAL);
X`7D
X
X/************************************************************/
Xreadlibdate(char *library,char *module,unsigned long date`5B2`5D)
X`7B
X  static unsigned long index = 0;
X  static unsigned long readop = 1;
X  static unsigned long buffer`5B50`5D;
X  static int initflag = TRUE;
X  static struct `7B
X    short int len;
X    short int typeclass;
X    char *addr;
X  `7D d = `7B 0, 0x010e, 0 `7D,
X  key = `7B 0, 0x010e, 0 `7D,
X  buf = `7B 200, 0x010e, &buffer `7D;
X  unsigned long s1, s2, rfa`5B2`5D;
X  char *l2, *l,*tmp;
X  int k2, k, buflen;
X
X  l = strperm(library);
X  k = strlen(library);
X  UPCASE(l,tmp);
X  if (initflag) `7B
X    d.addr = l;
X    d.len = k;
X    initflag = FALSE;
X    s1 = lbr$ini_control (&index, &readop, 0, 0);
X    s2 = lbr$open (&index, &d);
X    if (NOT (s1 & s2 & 0x01)) `7B
X      free (l);
X      initflag = TRUE;
X      return(FALSE);
X    `7D
X  `7D`09`09
X  else if (NOT strcmp (l, d.addr)) `7B
X    lbr$close (&index);
X    free (d.addr);
X    d.addr = l;
X    d.len = k;
+-+-+-+-+-+-+-+-  END  OF PART 1 +-+-+-+-+-+-+-+-
-+-+-+-+-+-+-+-+ START OF PART 2 -+-+-+-+-+-+-+-+
X    s1 = lbr$ini_control (&index, &readop, 0, 0);
X    s2 = lbr$open (&index, &d);
X    if (NOT (s1 & s2 & 0x01)) `7B
X      free (l);
X      initflag = TRUE;
X      return(FALSE);
X    `7D
X  `7D
X  l2 = strperm(module);
X  k2 = strlen(module);
X  UPCASE(l2,tmp);
X  key.len = k2;
X  key.addr = l2;
X  s1 = lbr$lookup_key (&index, &key, rfa);
X  if (s1 & 0x01)
X    s2 = lbr$set_module (&index, rfa, &buf, &buflen, 0);
X  free (l2);
X  if (NOT (s1 & 0x01) OR NOT (s2 & 0x01) OR buflen < 16)
X    return(FALSE);
X  date`5B0`5D = buffer`5B2`5D;
X  date`5B1`5D = buffer`5B3`5D;
X  return(TRUE);
X`7D
$ CALL UNPACK DATES.C;1 136279122
$ create 'f'
X/************************************************************/
Xparse_file_name(File *file);
Xbreakup_command_line(Command *command);
Xset_target_symbol(Target *target,char *name, char *value);
Xnew_target_symbol(Target *target,char *name,char *value);
Xincarnate(Target *deftarg,char *targname,char *pname,char *root);
Xinstantiate_rule(Target *deftarg,char *targname,char *pname, char *root);
XTarget *new_rule(char *name);
Xparse_symbol_line(char *line);
Xparse_target_line(char *line,char *targs`5B`5D,int *tnum,char *preqs`5B`5D,i
Vnt *pnum);
Xmatch_symbol(Symbol *symbol,char *name);
Xmatch_file(File *file,char *name);
Xmatch_prereq(Prereq *prereq,char *name);
Xmatch_target(Target *target,char *name);
XQentry *search_queue(Qentry *queue_header,char *value,int (*compare)());
Xinit_queue(Qentry *queue_header);
Xinclude(char *input);
Xisinclude(char *curline);
Xvoid perform_commands();
Xvoid learn_rules();
Xchar *substitute_symbols(char *input);
Xbuild(Target *target);
Xtry_default(char *targname);
Xmake(char *targname);
Xgetrevdate(File *file);
XFile *new_file(char *name);
Xadd_prereq(Target *targ, char *name);
Xadd_shell_line(Target *targ, char *line);
Xnew_symbol(char *name, char *value);
XTarget *new_target(char *name);
Xparse(FILE *infile);
Xmain(int argc,char **argv);
Xusage();
X/************************************************************/
Xchar *findwhite(char *string);
Xchar *findnonwhite(char *string);
Xchar *talloc(int i);
Xvoid error(char *errmsg);
Xpassword(char **p);
Xchar passnonsp(char **p);
Xchar passpace(char **p);
Xchar *strperm(char *s);
Xchar *mstrcat(char *p, char *q);
Xchar *lastnonspace(char *line);
Xchar *getline(FILE *stream);
$ CALL UNPACK FUNCTIONS.H;1 471493356
$ create 'f'
X/*
X * MAKE.C -- Main code for MAKE/VMS utility
X *
X *`09MAKE/VMS:
X *
X *`09VMS implementation of the UNIX(tm) MAKE utility
X *
X *`09(C)1986,1987,1988,1989
X *`09`09Todd Aven
X *`09`09the Software Sweatshop
X *`09`09564 Laurelton Boulevard
X *`09`09Long Beach, NY  11561
X *
X *`09This software may be freely copied and distributed with the
X *`09following stipulations:
X *
X *`09  o No fee except to recover costs of media and delivery may
X *`09    be charged for the use or possession of this software.
X *`09  o Sources to this utility must be made available in machine-
X *`09    readable form along with the executable form.
X *`09  o No portion of this program may be used in any program sold
X *`09    for a fee.
X *`09  o This copyright notice must not be removed.
X *
X */
X#define VMSVERS \
X"MAKE/VMS 3.4 (C)1986,1989 by Todd Aven, the Software Sweatshop, NY."
X
X#include <stdio.h>
X#include <ctype.h>
X#include <stat.h>
X#include <descrip.h>
X#include "clidef.h"
X#include <ssdef.h>
X#include <string.h>
X
X#include "simple.h"
X#include "make.h"
X#include "cmdline.h"
X#include "strings.h"
X#include "functions.h"
X
X/************************************************************/
X/*
X * MAKE parses the make file and constructs a somewhat tangled
X * directed graph describing dependencies.  There is a list of
X * TargNode structures, each of which points to a list of
X * preq_node structures.  These both point to _File structures,
X * which contain information about files such as date-time of write
X * and the filename.  To keep things simple, MAKE insures that only
X * one _File exists for any given file.
X */
X/************************************************************/
Xusage()
X`7B
X  puts(VMSVERS);
X  puts("This program may be copied freely for noncommercial purposes.");
X  puts("See the head of MAKE.C for full copyright information.\n");
X  puts("This program is an implementation for VAX/VMS of the MAKE utility");
X  puts("supplied with Unix.\n");
X  puts("Usage: MAKE `5Boptions ...`5D `5Btargets ...`5D");
X  puts("Options:");
X  puts("   /INPUT=<filename>  Specify makefile, default is MAKEFILE");
X  puts("   /OUTPUT=<filename> Specify name of output command file");
X  puts("   /IGNORE            Ignore errors while processing");
X  puts("   /EXECUTE           Negate to prevent execution of commands");
X  puts("   /DEBUG             Print debug info");
X  puts("   /KEEP              Keep command file");
X  puts("   /VERIFY            Echo commands");
X  puts("   /SPAWN             Don't wait for commands to complete");
X  puts("   /DEFAULTS          Use defaults file");
X  puts("   /FORCE             Always rebuild all targets");
X  exit(SS$_NORMAL);
X`7D
X/************************************************************/
Xmain(int argc,char **argv)
X`7B
X  int i, targcnt;
X  char *targnames`5BMAXTARGETS`5D;
X `20
X  targcnt = process_cmdline(targnames);
X  if(usage_flag) usage();
X
X  init_queue(&Default_listhead);
X  init_queue(&Target_listhead);
X  init_queue(&File_listhead);
X  init_queue(&Symbol_listhead);
X  new_symbol("CMSGEN","");
X
X  Dprintf("**************** Parsing rules file(s) ********************\n");
X  learn_rules();
X
X  Dprintf("****************** Making target(s) ***********************\n");
X  if(targcnt) `7B`09`09`09/* targets specified on command line */
X    for(i=0; i<targcnt; i++)
X      make(targnames`5Bi`5D);
X  `7D
X  else `7B
X    if(first_targ) `7B`09`09/* make first target in makefile */
X      make(first_targ->file->name);
X    `7D
X    else `7B
X      error("No targets specified");
X    `7D
X  `7D
X
X  if(cmd_file == NULL) `7B`09/* No command file was needed, so... */
X    printf("All targets were in synch.\n");
X    exit(SS$_NORMAL);
X  `7D
X
X  if(execute)
X    Dprintf("**************** Performing commands **********************\n")
V;`20
X  perform_commands();`09`09/* Never return from this one */
X`7D
X/************************************************************/
Xparse(FILE *infile)
X`7B
X  int i,j;
X  char *ptr;
X  char *curline;
X  char *targlist`5BMAXTARGETS`5D,*preqlist`5BMAXPREREQS`5D;
X  int targnum,preqnum;`09`09/* Num of targs and preqs in last targline */
X  Target *targ`5BMAXTARGETS`5D;`09/* List of targ pointers in last targline
V */
X  int first_nonwhite;
X
X  targnum = 0;`09`09`09/* Initially */
X
X  while(curline = getline(infile)) `7B
X    /* If this line is blank, skip it. */
X    if((first_nonwhite = strspn(curline,WHITESPACE)) == strlen(curline)) `7B
X      tfree(curline);
X      continue;`09`09`09/* Read next line */
X    `7D
X
X    /* Lines come in the following flavors (discovered in this order):
X     * 1 shell lines`09- Easy to spot: whitespace at the beginning
X     * 2 include lines`09- Easy to spot: 'INCLUDE' at the beginning
X     * 3 symbol_defs`09- Anything else and containing an '='
X     * 4 target lines`09- Anything else
X     */
X    if(first_nonwhite > 0) `7B`09/* If leading space, this is a shell line *
V/
X      if(targnum == 0)`09`09/* No targets yet */
X`09error("Target line must come before shell lines");
X      for(i = 0; i < targnum; i++)
X`09add_shell_line(targ`5Bi`5D,curline+first_nonwhite);
X      continue;`09`09`09/* Read next line */
X    `7D
X
X    /* Anything but a shell line we substitute symbols immediately */
X    ptr = substitute_symbols(curline);
X    tfree(curline);
X    curline = ptr;
X
X    /* Is it a directive? */
X    if(*curline == '%') `7B
X      parse_directive(curline + 1);
X      continue;`09`09`09/* Read next line */
X    `7D
X
X    /* Is it a symbol definition? */
X    if(strchr(curline,'=')) `7B
X      parse_symbol_line(curline);
X      continue;`09`09`09/* Read next line */
X    `7D
X
X    /* Can only be a 'targets : prerequisites' line. */
X
X    /* Collect the targets and prerequisites */
X    parse_target_line(curline,targlist,&targnum,preqlist,&preqnum);
X
X    /* Special handling for default rules */
X    if(strchr(targlist`5B0`5D,'*')) `7B
X      if(targnum != 1 AND preqnum <= 1)
X`09error("Default rules have one target and one or no prerequisite.");
X      targ`5B0`5D = new_rule(targlist`5B0`5D);
X      if(preqnum)
X`09add_prereq(targ`5B0`5D,preqlist`5B0`5D);
X      continue;
X    `7D
X
X    for(i=0; i<targnum; i++) `7B
X      /* If the target is not in the main list, add it. */
X      targ`5Bi`5D = LOOKUP_TARGET(targlist`5Bi`5D);
X      if(!targ`5Bi`5D) targ`5Bi`5D = new_target(targlist`5Bi`5D);
X      if(first_targ == NULL)`09/* First REAL target */
X`09first_targ = targ`5Bi`5D;
X      /* For each target, if prereqs are not in target's list, add 'em */
X      for(j=0; j<preqnum; j++) `7B
X`09if(!search_queue(&targ`5Bi`5D->Prereq_listhead,preqlist`5Bj`5D,match_prer
Veq))
X`09  add_prereq(targ`5Bi`5D,preqlist`5Bj`5D);
X      `7D
X    `7D`09`09`09`09/* Target line has been interned. */
X    tfree(curline);
X  `7D
X  fclose(infile);
X`7D
X/************************************************************/
XTarget *new_rule(char *name)
X`7B
X  Target *targ,*flink;
X
X  Dprintf("new_rule(\"%s\")\n", name);
X
X  /* Add new rule to top of list */
X  targ = (Target *)talloc(sizeof(Target));
X  targ->flink = Default_listhead.flink;
X  if(!(targ->file = LOOKUP_FILE(name)))
X    targ->file = new_file(name);
X  init_queue(&targ->Prereq_listhead);
X  init_queue(&targ->Command_listhead);
X  /* Link it into the list of rules */
X  Default_listhead.flink = targ;
X  return(targ);
X`7D
X/************************************************************/
XTarget *new_target(char *name)
X`7B
X  Target *targ,*flink;
X
X  Dprintf("new_target(\"%s\")\n", name);
X
X  /* Add new target to top of list */
X  targ = (Target *)talloc(sizeof(Target));
X  targ->flink = Target_listhead.flink;
X  if(!(targ->file = LOOKUP_FILE(name)))
X    targ->file = new_file(name);
X  init_queue(&targ->Prereq_listhead);
X  init_queue(&targ->Command_listhead);
X  init_queue(&targ->Symbol_listhead);
X  /* Link it into the list of targets */
X  Target_listhead.flink = targ;
X  return(targ);
X`7D
X/************************************************************/
Xnew_symbol(char *name, char *value)
X`7B
X  Symbol *sym;
X
X  Dprintf("\tSymbol \"%s\" added.\n",name);
X  sym = (Symbol *) talloc(sizeof(Symbol));
X  sym->name = strperm(name);
X  sym->value = strperm(value);
X  sym->flink = Symbol_listhead.flink;
X  Symbol_listhead.flink = sym;
X`7D
X/************************************************************/
Xset_target_symbol(Target *target,char *name, char *value)
X`7B
X  Symbol *sym;
X
X  Dprintf("Target symbol %s=%s\n",name,value);
X
X  if(!(sym = search_queue(&target->Symbol_listhead,name,match_symbol)))
X    new_target_symbol(target,name,value);
X  else `7B
X    tfree(sym->value);
X    sym->value = strperm(value);
X    Dprintf("\tSymbol \"%s\" replaced.\n",name);
X  `7D
X`7D
X/************************************************************/
Xnew_target_symbol(Target *target,char *name,char *value)
X`7B
X  Symbol *sym;
X
X  Dprintf("\tSymbol \"%s\" added.\n",name);
X  sym = (Symbol *) talloc(sizeof(Symbol));
X  sym->name = strperm(name);
X  sym->value = strperm(value);
X  sym->flink = target->Symbol_listhead.flink;
X  target->Symbol_listhead.flink = sym;
X`7D
X/************************************************************/
Xadd_shell_line(Target *targ, char *line)
X`7B
X  Command *cmdnode;
X  Command *q;
X `20
X  Dprintf("add_shell_line(\"%s\", \"%s\")\n", targ->file->name, line);
X
X  /* Attach command to END of list so commands are performed in order */
X  cmdnode = (Command *) talloc(sizeof(Command));
X  cmdnode->command = strperm(line);
X  /* Go to the end of the list */
X  q = &targ->Command_listhead;
X  while(q->flink)
X    q = q->flink;
X  cmdnode->flink = NULL;`09/* Make this node the end of the list */
X  q->flink = cmdnode;`09`09/* Add it to the list */
X`7D
X/************************************************************/
Xadd_prereq(Target *targ, char *name)
X`7B
X  Prereq *p,*q;
X
X  /* Allocate a Prereq node. */
X  p = (Prereq *)talloc(sizeof(Prereq));
X  /* If the file name is new, link a new file. */
X  if(!(p->file = LOOKUP_FILE(name))) `7B
X    p->file = new_file(name);
X  `7D
X
X /* Attach prereq to END of list so prereqs are made left-to-right */
X  q = &targ->Prereq_listhead;
X  while(q->flink)`09`09/* go to end of list */
X    q = q->flink;
X  q->flink = p;`09`09`09/* Q is now the last node in the list */
X  p->flink = NULL;
X`7D
X/************************************************************/
XFile *new_file(char *name)
X`7B
X  File *f;
X
X  Dprintf("new_file(\"%s\")\n", name);
X
X  f = (File *) talloc(sizeof(File));
X  f->name = strperm(name);
X  f->status = 0;
X  f->revdate = 0;
X  f->flink = File_listhead.flink;
X  File_listhead.flink = f;
X  return(f);
X`7D
X/************************************************************/
Xgetrevdate(File *file)
X`7B
X  File *lib;
X  struct stat stat_info;
X  long datetime, module_time = NOTIME;
X  int status;
X  char module`5B100`5D, library`5B100`5D;
X  unsigned long libdate`5B2`5D;
X
X  if(!(file->status & PARSED))
X    parse_file_name(file);
X
X  switch(file->type) `7B
X  case NORMAL_FILE:
X    if(stat(file->name, &stat_info) == 0) /* Stat returns 0 if OK */
X      module_time = stat_info.st_mtime;
X    break;
X  case LIBRARY_MODULE:
X    /* First, if the library doesn't exist we might be able to build it. */
X    if(!(lib = LOOKUP_FILE(file->library))) `20
X      lib = new_file(file->library);
X    if(!exists(lib->name) AND !(lib->status & BUILT)) `7B
X      Dprintf("Library %s doesn't exist. Trying to make it.\n",lib->name);
X      make(lib->name);
X      if(!(lib->status & BUILT))
X`09error(mstrcat("Couldn't find or build library ",lib->name));
X    `7D
X    /* Now get the module revdate */
X    if(readlibdate(file->library, file->module, libdate)) `7B
X      module_time = SHELL$FIX_TIME(libdate);
X    `7D
X    break;
X  case CMS_MODULE:
X    /* Get the module revdate */
+-+-+-+-+-+-+-+-  END  OF PART 2 +-+-+-+-+-+-+-+-
-+-+-+-+-+-+-+-+ START OF PART 3 -+-+-+-+-+-+-+-+
X    if(readcmsdate(file->library,cms_generation,file->module,libdate)) `7B
X      module_time = SHELL$FIX_TIME(libdate);
X    `7D
X    break;
X  default:
X    error(mstrcat("Unrecognized file type for file ",file->name));
X  `7D
X
X  Dprintf("Module %s revision time: %s",file->name,ctime(&module_time));
X  file->status `7C= DATED;
X  file->revdate = module_time;
X`7D
X/************************************************************/
X/*
X * make(name)
X *
X *      This routine actually does the work.  It scans the list of
X *      targets parsed from the makefile, and checks the target's
X *      prerequisites date/time values against the target's.  If
X *      the prerequisite is itself a target(present in target_list),
X *      we call make recursively to check it.  Then, if any of our
X *      prerequisites are newer than we are, we execute all our shell
X *      commands.  If there are no prerequisites specified at all, then
X *      also execute all our shell commands.
X */
Xmake(char *targname)
X`7B
X  Target *target;
X  Prereq *prereq;
X  long targtime;
X  long date;
X  long newest = NOTIME;
X
X  Dprintf("Making %s\n", targname);
X
X  target = LOOKUP_TARGET(targname);
X  if(!target) `7B
X    try_default(targname);
X  `7D
X  else `7B
X    /* To add a dependency 'clause' to a default rule, provide no
X     * command lines.
X     */
X    if(EMPTY_LIST(target->Command_listhead))
X      try_default(targname);
X  `7D
X  /* Lookup target again. Default rules 'create' new targets. */
X  target = LOOKUP_TARGET(targname);
X  if(!target) `7B`09`09`09/* It's not a target, so chain ends here */
X    if(exists(targname))
X      return;
X    else
X      error(mstrcat(targname," doesn't exist and couldn't be made."));
X  `7D
X `20
X  prereq = target->Prereq_listhead.flink;
X  /* Now we proceed up the tree... make each prerequisite. */
X  while(prereq) `7B
X    /* If the branch has not been climbed, do it now */
X    if(!(prereq->file->status & BUILT)) `7B /* See if it needs building */
X       make(prereq->file->name); /* Try making the prereqs */
X       /* Get the revision date of the module */
X       if(!(prereq->file->status & DATED))
X`09 getrevdate(prereq->file);
X       date = prereq->file->revdate;
X     `7D
X     `20
X    if(prereq->file->status & BUILT)
X      date = FORCE;`09`09/* FORCE propagates down the tree */
X    /* If it doesn't exist, force making target */
X    if(date == NOTIME)
X      date = FORCE;
X
X    /* Keep the date of the 'newest' prerequisite. */
X    if(date > newest) newest = date;
X    prereq = prereq->flink;`09/* Hit the next branch */
X  `7D
X
X  /* Get the date of the target */
X  if(!(target->file->status & DATED))
X    getrevdate(target->file);
X
X  targtime = target->file->revdate;
X
X  /* Force building if the target has no prerequisites and doesn't exist */
X  if(EMPTY_LIST(target->Prereq_listhead) AND !exists(target->file->name))
X    newest = FORCE;
X
X  Dprintf("Target \"%s\" datetime is %08lx, newest prereq is %08lx\n",
X`09    target->file->name, targtime, newest);
X
X  if((targtime < newest) OR alwaysrebuild) `7B
X    if(!EMPTY_LIST(target->Command_listhead))
X      build(target);
X    target->file->status `7C= BUILT;
X  `7D
X`7D
X/************************************************************/
X/* try_default
X *
X *`09Try a default rule for target for one of two reasons:
X *
X *`09a) A prerequisite is not itself a target. There may be
X *`09   a default rule describing how to make the prerequisite.
X *
X *`09b) A target had at least one prerequisite, but there were
X *`09   no shell lines to tell how to make it, so there might
X *`09   be a default rule to tell how (otherwise, we've got
X *`09   a boo-boo). For instance, we might have a default rule
X *  `09`09*.obj : *.c
X *`09`09`09cc $*
X *`09   for C programs, but this C program depends on an
X *`09   include file
X *`09`09thisfile.obj : thisfile.h
X *
X *`09There are two ways a rule can match:
X *`09`09o EXACTLY -- target matches a rule, AND the prereq exists.
X *`09`09o CLOSELY -- target matches a rule.
X */
Xtry_default(char *targname)
X`7B
X  char pname`5B100`5D,root`5B75`5D;
X  Target *deftarg;
X  Prereq *prereq;
X  Target *first_match;
X  char *star;
X  int i,offset;
X  int matched;
X
X  Dprintf("try_default(\"%s\")\n",targname);
X
X  first_match = NULL;
X  deftarg = &Default_listhead;
X  while(deftarg = deftarg->flink) `7B
X    Dprintf("...trying '%s'...",deftarg->file->name);
X
X    /* Manually compare up to the wildcard. SHELL$MATCH_WILD won't. */
X    star = strchr(deftarg->file->name,'*');
X    offset = star - deftarg->file->name;
X    matched = (offset > 0);`09/* False if '*' is first character. */
X    /* If targname is shorter, we'll stop on the NULL. */
X    for(i=0; i<offset; i++) `7B`09/* Pass over if '*' is first character. */
X      if(*(targname+i) != *(deftarg->file->name+i)) `7B
X`09matched = 0;
X`09break;
X      `7D
X    `7D
X
X    if(!matched &&
X       !shell$match_wild(targname+offset,deftarg->file->name+offset)) `7B
X`09 Dprintf("failed.\n");
X`09 continue;`09`09`09/* This rule didn't match the target */
X       `7D
X
X    Dprintf("matched.\n");
X
X    if(!first_match)`09`09/* Remember first 'CLOSELY' matched rule */
X      first_match = deftarg;
X
X    /* If there's no prerequisite, it can't match 'EXACTLY' */
X    if(!(prereq = deftarg->Prereq_listhead.flink))
X      continue;`09`09`09/* No prereq for this target */
X `20
X    /* Get the prerequisite name from the target name and the rule */
X    incarnate(deftarg,targname,pname,root);
X
X    if(!exists(pname))`09`09/* If it doesn't exist, not an 'exact' match */
X      continue;
X
X    /* We've matched 'EXACTLY'. Let's put the incarnated rule in the list */
X    instantiate_rule(deftarg,targname,pname,root);
X    return(TRUE);`09`09/* We matched EXACTLY */
X  `7D
X  if(!first_match)`09`09/* No rule matched, even CLOSELY */
X    return(FALSE);
X  incarnate(first_match,targname,pname,root);
X  instantiate_rule(first_match,targname,pname,root);
X`7D
X/************************************************************/
Xincarnate(Target *deftarg,char *targname,char *pname,char *root)
X`7B
X  char *ptr,*optr,*asterisk;
X  Prereq *prereq;
X  int left_length, middle_length, right_length;
X
X  Dprintf("incarnate(\"%s\",\"%s\",,)\n",deftarg->file->name,targname);
X
X  prereq = deftarg->Prereq_listhead.flink;
X  *pname = '\0';`09`09/* There might not be a prerequisite */
X
X  /* Break the real target into three pieces:
X   *    1. Left of '*'
X   *    2. Right of '*'
X   *    3. The part that matches '*'
X   */
X
X  asterisk = strchr(deftarg->file->name,'*');
X  left_length = asterisk - deftarg->file->name;
X  right_length = strlen(asterisk+1);
X  middle_length = strlen(targname) - left_length - right_length;
X  ptr = targname + left_length;
X
X  /* Get the root name from the real target */
X  strncpy(root,ptr,middle_length);
X  root`5Bmiddle_length`5D = '\0';
X
X  if(!prereq)`09`09`09/* If no prereq, don't bother with the rest */
X    return;
X
X  /* Replace the '*' in the prerequisite with the root name. */
X  ptr = prereq->file->name;
X  optr = pname;
X  while(*ptr != '*') *(optr++) = *(ptr++);
X  strcpy(optr,root);`09`09/* Insert the root name */
X  ptr++;
X  Dprintf("Incarnate(): left + root = \"%s\"\n",pname);
X  strcat(pname,ptr);`09`09/* Add the rest */
X  Dprintf("Incarnate(): rest = \"%s\"\n",ptr);
X  return;
X`7D
X/************************************************************/
Xinstantiate_rule(Target *deftarg,char *targname,char *pname, char *root)
X`7B
X  Target *newtarg;
X  char symline`5B255`5D;
X  Command *defnode,*lastnode,*newnode;
X
X  if(!(newtarg = LOOKUP_TARGET(targname))) /* Not necessarily a new target *
V/
X    newtarg = new_target(targname);
X  if(strlen(pname))
X    add_prereq(newtarg,pname);
X  /* Even if target existed before this, there were no commands */
X  /* COPY default rule's command list to newly-instantiated command list */`
V20
X  defnode = &deftarg->Command_listhead;
X  lastnode = &newtarg->Command_listhead;
X  while(defnode = defnode->flink) `7B /* Traverse the deftarg's command list
V */
X    newnode = (Command *)talloc(sizeof(Command));
X    newnode->command = strperm(defnode->command);
X    lastnode->flink = newnode;`09/* Link it into the list */
X    newnode->flink = NULL;
X    lastnode = newnode;
X  `7D
X  /* Define symbols which refer to the current default rule match */
X  set_target_symbol(newtarg,"*",root);
X  set_target_symbol(newtarg,"@",newtarg->file->name);
X  set_target_symbol(newtarg,"<",pname);
X`7D
X/************************************************************/
X/*
X * build
X *
X *      Write DCL commands for a target that needs to be built
X */
Xbuild(Target *target)
X`7B
X  char symline`5B8000`5D;
X  Symbol *sym;
X  Command *cmdnode;
X  char *cmd,*ptr;
X  char tmpbuf`5B8000`5D;
X  char cmdbuf`5B8000`5D;
X  int cont_line,input_line;
X `20
X  if(cmd_file == NULL) `7B`09/* Command file hasn't yet been opened. */
X    if(cmd_fname == NULL) cmd_fname = strperm(mktemp("makeXXXXXXXX.com"));
X    cmd_file = fopen(cmd_fname,"w");
X    tfree(cmd_fname);
X    cmd_fname = strperm(fgetname(cmd_file,tmpbuf));
X    Dprintf("Opening command file \"%s\"\n", cmd_fname);
X    if(verify)
X      fputs("$ Verify_Flag = F$Verify(1)\n", cmd_file);
X    fputs("$ On Control_Y Then Goto The_Exit\n",cmd_file);
X    if(ignore_errors)
X      fputs("$ set noon\n", cmd_file);
X    else
X      fputs("$ On Error Then Goto The_Exit\n",cmd_file);
X  `7D
X
X  /* Define any of this target's local symbols in the global list now */
X  sym = &target->Symbol_listhead;
X  while(sym = sym->flink) `7B
X    sprintf(symline,"%s=%s",sym->name,sym->value);
X    parse_symbol_line(symline);
X  `7D
X
X  cont_line = FALSE;`09`09/* True when previous line ended with '-' */
X `20
X  cmdnode = target->Command_listhead.flink;
X  while(cmdnode) `7B
X    cmd = substitute_symbols(cmdnode->command);
X    tfree(cmdnode->command);
X    cmdnode->command = strperm(cmd);
X    breakup_command_line(cmdnode); /* Break lines at '\' */
X    cmd = cmdnode->command;
X    input_line = (*lastnonspace(cmd) == '$');
X    if(input_line)`09`09/* Get rid of the '$' now */
X      *strrchr(cmd,'$') = '\0';
X    if(cont_line `7C`7C input_line)
X      cmdbuf`5B0`5D = '\0';`09`09/* No '$ ' at beginning of these lines */
X    else
X      strcpy(cmdbuf,"$ ");`09/* Make it a normal DCL line */
X    strcat(cmdbuf,cmd);
X    strcat(cmdbuf,"\n");
X    cont_line = (*lastnonspace(cmd) == '-');
X    Dprintf("Putting \"%s\" into command file.\n", cmdbuf);
X    fputs(cmdbuf, cmd_file);
X    tfree(cmd);
X    cmdnode = cmdnode->flink;
X  `7D
X`7D
X/************************************************************/
Xchar *substitute_symbols(char *input)
X`7B
X  char output`5B8000`5D;
X  char *iptr,*dollar;
X  char *lgroup,*rgroup,*next;
X  char symbol`5B8000`5D;
X  Symbol *sym;
X
X  Dprintf("substitute_symbols(\"%s\")\n",input);
X  iptr = input;
X  output`5B0`5D = '\0';
X  while(dollar = strchr(iptr,'$')) `7B
X    next = dollar + 1;
X    if(!isspace(*next) AND *next != '\0') `7B /* Maybe a symbol */
X      if(*next == '(' OR *next == '`7B') `7B /* It's definitely a symbol */
X`09lgroup = next + 1;`09/* Beginning of symbol */
X`09if(!(rgroup = strpbrk(lgroup,")`7D")))
X`09  error("Missing right paren on macro");
X`09*rgroup = '\0';
X`09strcpy(symbol,lgroup);
X`09*rgroup = '`7D';
X`09if(sym = LOOKUP_SYMBOL(symbol)) `7B
X`09  *dollar = '\0';
X`09  strcat(output,input);
X`09  *dollar = '$';
X`09  strcat(output,sym->value);
X`09  input = rgroup + 1;
X`09  iptr = input;
X`09`7D
X`09else
X`09  error(mstrcat("Undefined symbol ",symbol));
X      `7D
X      else `7B`09`09`09/* Maybe a single-char symbol */
X`09symbol`5B0`5D = *next;
X`09symbol`5B1`5D = '\0';
X`09if(sym = LOOKUP_SYMBOL(symbol)) `7B
X`09  *dollar = '\0';
X`09  strcat(output,input);`09/* Take everything up to the symbol */
X`09  *dollar = '$';
X`09  strcat(output,sym->value); /* Take the symbol's value */
X`09  input = next + 1;`09/* Drop what we've already moved to output */
X`09  iptr = input;`09`09/* Look at the beginning of new segment */
X`09`7D
X`09else `7B
X`09  iptr = next;`09`09/* This '$' was not a symbol signifier */
X`09`7D
X      `7D
X    `7D
X    else `7B
X      iptr = next;`09`09/* This '$' was not a symbol signifier */
X    `7D
X  `7D
X  strcat(output,input);
X  Dprintf("\treturn(\"%s\")\n",output);
X  return(strperm(output));
X`7D
X/************************************************************/
X/* learn_rules
X *
X *`09Parse the appropriate rules files. Try make.ini in the current
X *`09directory first, then in the home directory. Also try`20
X *`09makefile in the current directory if no file was specified on
X *`09the command line, else use the specified file.
X */
Xvoid learn_rules()
X`7B
X  FILE *makefile;
X
X  if(usedefaults) `7B
X    makefile = fopen(MAKE_DEFAULTS, "r"); /* Try MAKE_DEFAULTS. */
X    if(!makefile)
X      makefile = fopen("make.ini", "r"); /* Try MAKE.INI */
X    if(makefile)
X      parse(makefile);`09`09/* If one exists, parse it. */
X  `7D
X  first_targ = NULL;`09/* No default targets allowed from MAKE_DEFAULTS */
X  if(makefile = fopen(makefilename, "r"))
X    parse(makefile);
X`7D
X/************************************************************/
Xvoid perform_commands()
X`7B
X#define CLI$M_NOWAIT 0x00000001
X#define CLI$M_NOTIFY 0x00000010
X  register unsigned long spawn_flags = CLI$M_NOWAIT `7C CLI$M_NOTIFY;
X  char *kill_string = "$ Delete/NoLog 'F$Environment(\"PROCEDURE\")'\n";
X  strdesc cmd_descrip;
X  char *cmd_string;
X  unsigned long status;
X
X  cmd_string = mstrcat("@", cmd_fname); /* Now that we know the name. */
X  fputs("$The_Exit:\n", cmd_file);
X  fputs("$ Save_Status = $STATUS\n", cmd_file);
X  if(!keep) fputs(kill_string, cmd_file);
X  if(verify) fputs("$ Verify_Flag = F$Verify(Verify_Flag)\n", cmd_file);
X  fputs("$ exit Save_Status\n", cmd_file);
X  fclose(cmd_file);
X  if(execute) `7B
X    DESCRIP(cmd_descrip,cmd_string);
X    if(spawn) `7B
X      if(verify) printf("$ Spawn/NoWait/Notify %s\n", cmd_string);
X      status = lib$spawn(&cmd_descrip, NULL, NULL, &spawn_flags);
X      if(!(status & 1))
X`09lib$signal(status);
X    `7D
X    else `7B
X      if(verify) printf("$ %s\n", cmd_string);
+-+-+-+-+-+-+-+-  END  OF PART 3 +-+-+-+-+-+-+-+-
-+-+-+-+-+-+-+-+ START OF PART 4 -+-+-+-+-+-+-+-+
X      /* lib$do_command never returns unless there's an error */
X      lib$signal(lib$do_command(&cmd_descrip));
X    `7D
X  `7D
X`7D
X/************************************************************/
Xinclude(char *name)
X`7B`20
X  FILE *incfile;
X
X  Dprintf("Reading include file \"%s\"\n",name);
X  if(incfile = fopen(name,"r"))
X    parse(incfile);
X  else
X    error(mstrcat("Couldn't open include file ",name));
X`7D
X/************************************************************/
Xinit_queue(Qentry *queue_header)
X`7B
X  queue_header->flink = NULL;
X`7D
X/************************************************************/
XQentry *search_queue(Qentry *queue_header,char *value,int (*compare)())
X`7B
X  Qentry *p = queue_header->flink;
X  while(p) `7B
X    if((*compare)(p,value))
X      return(p);
X    else
X      p = p->flink;
X  `7D
X  /* No entry matched */
X  return(NULL);
X`7D
X/************************************************************/
Xmatch_target(Target *target,char *name)
X`7B
X  if(0 == strcmp(target->file->name,name))
X    return(TRUE);
X  else
X    return(FALSE);
X`7D
X/************************************************************/
Xmatch_prereq(Prereq *prereq,char *name)
X`7B
X  return(0 == strcmp(prereq->file->name,name));
X`7D
X/************************************************************/
Xmatch_file(File *file,char *name)
X`7B
X  return(0 == strcmp(file->name,name));
X`7D
X/************************************************************/
Xmatch_symbol(Symbol *symbol,char *name)
X`7B
X  return(0 == strcmp(symbol->name,name));
X`7D
X/************************************************************/
Xparse_target_line(char *line,char *targs`5B`5D,int *tnum,char *preqs`5B`5D,i
Vnt *pnum)
X`7B
X  char *ptr,*part;
X  char *left,*right;
X  char *slash;
X
X  /* First change ',' and '\' into ' '. */
X  for(ptr = line; *ptr; ptr++) `7B
X    if(*ptr == ',') *ptr = ' ';`09/* Allow commas as separators */
X    if(*ptr == '\\') *ptr = ' '; /* Backslash is an implicit separator */
X  `7D
X
X  part = line;
X  while(ptr = strchr(part,':')) `7B
X    if(isspace(*(ptr+1)) OR (*(ptr+1) == '\0')) `7B
X      *ptr = '\0';
X      left = strperm(line);
X      ptr++;
X      right = strperm(ptr);
X      *(ptr-1) = ':';
X      break;
X    `7D
X    part = ptr + 1;
X  `7D
X  if(!ptr)
X    error("No separating ':'");
X
X  *tnum = 0;
X  part = left;
X  while(ptr = findnonwhite(part)) `7B
X    if(part = findwhite(ptr))
X      *part = '\0';
X    if(slash = strchr(ptr,'/'))
X      *slash = '\0';`09`09/* For file names such as foo.opt/opt */
X    targs`5B(*tnum)++`5D = strperm(ptr);
X    if(part)
X      *part = ' ';
X    else
X      break;
X  `7D
X  *pnum = 0;
X  part = right;
X  while(ptr = findnonwhite(part)) `7B
X    if(part = findwhite(ptr))
X      *part = '\0';
X    if(slash = strchr(ptr,'/'))
X      *slash = '\0';`09`09/* For file names such as foo.opt/opt */
X    preqs`5B(*pnum)++`5D = strperm(ptr);
X    if(part)
X      *part = ' ';
X    else
X      break;
X  `7D
X  tfree(left);
X  tfree(right);
X`7D
X/************************************************************/
Xparse_symbol_line(char *line)
X`7B
X  char *eq,*sp;
X  char tmpbuf`5B8000`5D;
X  char *left,*right;
X  char symbol`5B8000`5D,value`5B8000`5D;
X  Symbol *sym;
X
X  left = line;
X  right = strchr(line,'=');
X  *(right++) = '\0';
X  sscanf(left," %s ",symbol);
X  right = findnonwhite(right);`09/* Remove leading spaces */
X  if(right)
X    strcpy(value,right);`09/* It's EVERYTHING to the right of the '=' */
X  else
X    strcpy(value,"");`09`09/* It's a null symbol */
X
X  Dprintf("Symbol: %s=%s\n",symbol,value);
X
X  if(sym = LOOKUP_SYMBOL(symbol)) `7B /* If it exists, replace the value */
X    Dprintf("\tSymbol \"%s\" redefined.\n",symbol);
X    tfree(sym->value);
X    sym->value = strperm(value);
X  `7D
X  else `7B`09`09`09/* Just add it to the list */
X    new_symbol(symbol,value);
X  `7D
X`7D
X/************************************************************/
Xbreakup_command_line(Command *cmdnode)
X`7B
X  char *ptr,*oldcmd;
X  Command *newnode;
X  int broke_one = FALSE;
X
X  oldcmd = cmdnode->command;`09/* Don't lose the original command */
X
X  /* Prune new commands off the end of the original command at '\' */
X  while(ptr = strrchr(oldcmd,'\\')) `7B /* As long as we have a slash */
X    broke_one = TRUE;
X    newnode = (Command *)talloc(sizeof(Command));
X    newnode->command = strperm(ptr + 1);
X    newnode->flink = cmdnode->flink; /* Insert this line before next node */
X    cmdnode->flink = newnode;
X    *ptr = '-';`09`09`09/* Change to a DCL continuation char */
X    *(ptr+1) = '\0';`09`09/* Split after the continuation */
X  `7D
X  if(broke_one) `7B`09`09/* Clean up the first line */
X    cmdnode->command = strperm(oldcmd);`09/* It's shorter now */
X    tfree(oldcmd);
X  `7D
X`7D
X/************************************************************/
Xparse_file_name(File *file)
X`7B
X  char *rparen,*special;
X
X  special = strpbrk(file->name,"`7E("); /* Special 'file' signifiers */
X
X  file->status `7C= PARSED;
X  file->type = NORMAL_FILE;`09/* By default */
X
X  if(!special)
X    return;
X
X  switch(*special) `7B
X  case '(':
X    file->type = LIBRARY_MODULE; /* Syntax <library>(<module>) */
X    if((rparen = strchr(file->name,')')) < special)
X      error(mstrcat("Syntax error in filename ",file->name));
X    *special = '\0';
X    file->library = strperm(file->name);
X    *special = '(';
X    *rparen = '\0';
X    file->module = strperm(special+1);
X    *rparen = ')';
X    break;
X  case '`7E':
X    file->type = CMS_MODULE;`09/* Syntax: <module>`7E<library> */
X    *special = '\0';
X    file->module = strperm(file->name);
X    if(strlen(special+1)) `7B
X      file->library = strperm(special+1);
X    `7D
X    else `7B
X      if(cms_default)
X`09file->library = cms_default;
X      else
X`09error(mstrcat("No default CMS library for ",file->module));
X    `7D
X    *special = '`7E';
X    break;
X  `7D
X`7D
X/************************************************************/
Xexists(char *name)
X`7B
X  File *file = LOOKUP_FILE(name);
X
X  if(!file) file = new_file(name);
X
X  if(!(file->status & DATED))
X    getrevdate(file);
X  if(file->revdate == NOTIME) `7B
X    Dprintf("Object doesn't exist: \"%s\"\n",file->name);
X    return(FALSE);
X  `7D
X  else `7B
X    Dprintf("Object exists: \"%s\"\n",file->name);
X    return(TRUE);
X  `7D
X`7D
X/************************************************************/
Xparse_directive(char *line)
X`7B
X  char tmpbuf`5B8000`5D;
X  char *directive = line;
X  char *object = NULL;
X  char *end;
X
X  /* Lines in the file like %DIRECTIVE `5B<object>`5D */
X  if(end = findwhite(directive)) `7B
X    *end = '\0';
X    object = findnonwhite(end+1);
X  `7D
X
X  Dprintf("Parsing directive %s\n",directive);
X
X  /* Is it an 'INCLUDE <file>' line? */
X  if(strcmp(directive,"INCLUDE") == 0) `7B
X    if(!object)
X      error("No INCLUDE file specified");
X    include(object);
X  `7D
X  else if(strcmp(directive,"CMSLIB") == 0) `7B
X    if(!object)
X      error("No CMS library specified");
X    end = findwhite(object);
X    if(end)
X      *end = '\0';
X    tfree(cms_default);
X    cms_default = strperm(object);
X    sprintf(tmpbuf,"CMSLIB = %s",cms_default);
X    parse_symbol_line(tmpbuf);
X  `7D
X  else if(strcmp(directive,"CMSGEN") == 0) `7B
X    if(!object)
X      error("No CMS generation specified");
X    end = findwhite(object);
X    if(end)
X      *end = '\0';
X    tfree(cms_generation);
X    cms_generation = strperm(object);
X    sprintf(tmpbuf,"CMSGEN = /GEN=%s",cms_generation);
X    parse_symbol_line(tmpbuf);
X  `7D
X  else if(strcmp(directive,"DEBUG") == 0)
X    debug = TRUE;
X  else if(strcmp(directive,"NODEBUG") == 0)
X    debug = FALSE;
X  else if(strcmp(directive,"SPAWN") == 0)
X    spawn = TRUE;
X  else if(strcmp(directive,"NOSPAWN") == 0)
X    spawn = FALSE;
X  else
X    error("Unrecognized DIRECTIVE.");
X`7D
$ CALL UNPACK MAKE.C;11 388917000
$ create 'f'
Xtypedef struct _Qentry `7B
X  struct _Qentry *flink;
X`7D Qentry;
X
Xtypedef enum `7BNORMAL_FILE,LIBRARY_MODULE,CMS_MODULE`7D FileType;
X
Xtypedef struct `7B
X  Qentry *flink;
X  char *name;
X  int status;
X  int revdate;
X  FileType type;
X  char *module;
X  char *library;
X`7D File;
X
X#define DATED 1<<0`09`09/* Bit set when we've checked the date */
X#define BUILT 1<<1`09`09/* Bit set when we've performed a build */
X#define PARSED 1<<2`09`09/* Bit set when we've parsed the name */
X
Xtypedef struct `7B
X  Qentry *flink;
X  File *file;
X  Qentry Prereq_listhead;
X  Qentry Command_listhead;
X  Qentry Symbol_listhead;
X`7D Target;
X
Xtypedef struct `7B
X  Qentry *flink;
X  File *file;
X`7D Prereq;
X
Xtypedef struct `7B
X  Qentry *flink;
X  char *command;
X`7D Command;
X
Xtypedef struct `7B
X  Qentry *flink;
X  char *name;
X  char *value;
X`7D Symbol;
X
X#define MAKE_DEFAULTS "make_defaults"
X#define MAXTARGETS 100`09`09/* Max targets on any one line. */
X#define MAXPREREQS 100`09`09/* Max prereqs on any one line. */
X
X#define NOTIME 0x00000000L`09/* Value returned by stat() if file
X`09`09`09`09   doesn't exist. */
X#define FORCE  0x7fffffffL`09/* Force Target to be made */
X
X#define LOOKUP_TARGET(X) search_queue(&Target_listhead,X,match_target)
X#define LOOKUP_SYMBOL(X) search_queue(&Symbol_listhead,X,match_symbol)
X#define LOOKUP_FILE(X) search_queue(&File_listhead,X,match_file)
X
X#define EMPTY_LIST(X) (X.flink == NULL)
X
X/* Four global lists: Defaults, Targets, Files, and Symbols */
XQentry Default_listhead;
XQentry Target_listhead;
XQentry File_listhead;
XQentry Symbol_listhead;
X
XFILE *cmd_file = NULL;
XTarget *first_targ = NULL;
Xchar *cms_default = NULL;
Xchar *cms_generation = NULL;
X
X#define SEPARATOR " \t,"
X#define WHITESPACE " \t"
$ CALL UNPACK MAKE.H;1 408565437
$ create 'f'
X1 MAKE
X MAKE/VMS is a utility which  performs commands specified  by the user
X when  a  dependency  relationship  specified  by   the   user is  not
X satisfied. This is useful in situations such  as software development
X where   the  user is  in an  edit-compile-test   cycle and wishes  to
X simplify the steps between 'edit' and 'test'. Many  other  tasks lend
X themselves well to the MAKE structure.
X
X $ MAKE `5BQualifier(s)`5D `5BTarget(s)`5D
X2 Qualifiers
X/INPUT=<file>
X
X Choose an alternate makefile. The default value is "MAKEFILE".
X
X/OUTPUT=<file>
X
X Specify the name  of the command   file.  Use '/KEEP'   also   if you
X actually wish for the file to remain.
X
X/IGNORE
X /IGNORE
X /NOIGNORE (default)
X
X Ignore errors in the command file (via the command 'SET NOON').
X
X/EXECUTE
X /EXECUTE (default)
X /NOEXECUTE
X
X Execute the commands needed to  update the targets.   The negation of
X this qualifier is useful in order to obtain a static command file via
X a combination of qualifiers  such as /KEEP,  /FORCE, and  /OUTPUT for
X later use.
X
X/DEBUG
X /DEBUG
X /NODEBUG (default)
X
X Print debugging info (for bug reports).  Much of  the action  of MAKE
X may be discerned from this output.
X
X/KEEP
X /KEEP
X /NOKEEP (default)
X
X Prevent deletion of the command file generated by running MAKE.
X
X/VERIFY
X /VERIFY
X /NOVERIFY (default)
X
X Echo commands as they execute (via the command 'SET VERIFY').
X
X/SPAWN
X /SPAWN
X /NOSPAWN (default)
X
X The default action of  MAKE is to call  the routine LIB$DO_COMMAND to
X execute the command file (if any) that has been built during the Make
X phase. Use of this qualifier directs MAKE to  perform a  SPAWN/NOWAIT
X instead.
X
X/DEFAULTS
X /DEFAULTS (default)
X /NODEFAULTS
X
X Control reading of the default rules file MAKE_DEFAULTS or MAKE.INI.
X
X/FORCE
X
X Always rebuild all targets, even if they are in synch.
X2 Description
X MAKE is  a program which  reads a file  of  dependencies and executes
X commands when one file  is out of  date  with respect to  one or more
X files upon which   it depends.  For instance,  an   executable  image
X FOO.EXE depends upon  FOO.OBJ,  which in turn  depends upon FOO.C and
X FOO.H.  If FOO.H is modified,  then  it is  newer than FOO.OBJ and so
X FOO.OBJ needs to be remade.  Because  FOO.OBJ is remade, FOO.EXE will
X become out of date  and  must  be remade  subsequently.  MAKE   reads
X user-supplied description files and executes any commands required to
X bring the target(s) up to date with respect to all dependents.
X3 Parsing
X MAKE  takes   as input one    or   more description    files   which,
X appropriately enough, describe the relationships  between targets and
X sources. An attempt is made to open the file MAKE_DEFAULTS, which may
X be a logical  name. If this  fails, the file MAKE.INI  in the current
X directory is attempted.    After the initialization  file   is parsed
X (only the first encountered will be used), an attempt is made to open
X MAKEFILE. This, too, may be a logical name. Alternatively,  it may be
X specified on the command line with the /INPUT qualifier.  There is no
X explicit  requirement to  access  a makefile  successfully,   but the
X requirement of  finding at least one  target  implicitly requires  at
X least one makefile to be accessed.
X
X As the files are read in line by line, they are internalized into the
X various data  structures used by  MAKE, such as the Symbol  list, the
X Target list, the File list,  et al. Output  from the /DEBUG qualifier
X demonstrates the parsing phase in gory detail.
X3 Making
X After all makefiles  have  been digested, one  or  more  targets  are
X 'made'. If any targets  were  specified on the command  line, each is
X 'made' in turn.  If no targets  were specified, the first  bona  fide
X target encountered during the parsing phase is 'made'.
X
X A target is 'made'  by making in turn each  of its prerequisites.  If
X you  are  thinking that this  is a  recursive definition, you   might
X consider becoming  a  programmer.  When  MAKE reaches  the end   of a
X branch, it climbs back down a level in the tree comparing the date of
X the current level's target to  the newest dependent. If  a target  is
X older than at least one of its prerequisites (and a missing target is
X considered infinitely old),  it is 'built' according  to the commands
X that were supplied with the dependency rule being examined.
X
+-+-+-+-+-+-+-+-  END  OF PART 4 +-+-+-+-+-+-+-+-
-+-+-+-+-+-+-+-+ START OF PART 5 -+-+-+-+-+-+-+-+
X A  target  is  out  of date  if it  is newer  than  a  dependent, but
X obviously the more general case has a  target out of  date if it does
X not  even exist.   Also,  one    can create  a  target  that   has no
X dependents, a so-called 'terminal target'.  Terminal targets exist or
X don't exist, but can't be out of date.  If a terminal target does not
X exist, it will be 'built' (the commands for the target will be  added
X to the command file).   If it exists,  fine.  If  any dependent of  a
X target is built, then  the target must  be built as  well.  Since the
X 'making' is recursive, the leaves of the tree  are checked before the
X branches so that this forces 'building' to propagate back to the root
X target.
X4 Normal files
X The comparison date for normal files is the revision date of the file
X as returned  by the  VAXCRTL 'stat' function.   This can  be examined
X from DCL using the DIR/DAT=MOD command.
X4 Library modules
X The comparison date for library modules (syntax =  'lib.typ(mod)') is
X the date  of last   modification of the  module as  returned   by the
X LBR$SET_MODULE routine. Note  that  libraries may  be  of   any  type
X acceptable to the VAX Librarian facility (.OLB, .TLB, .MLB, etc.).
X4 CMS elements
X The comparison date for CMS elements is the date of last modification
X for the  generation of the  element in the  specified  class, or  the
X newest generation along the  main  line   of descent if  no  class is
X specified.
X3 Building
X Building a  target   consists  of placing    the  commands,  suitably
X formatted, into  a command file for   later execution. When the  Make
X phase completes, the command file is closed and it is executed (using
X LIB$DO_COMMAND)  in the current  process, unless the /SPAWN qualifier
X was  specified  or the %SPAWN  directive  was encountered  without  a
X countermanding %NOSPAWN.  Simple error handling  is included  in  the
X command file to cause command processing to  terminate when an  error
X (DCL ERROR severity) is encountered. Also, the  command file contains
X a command to delete itself  upon exiting. This can be  prevented  via
X the /KEEP qualifier.
X2 Syntax
X The description file is called a 'MAKEFILE',  as in the UNIX version.
X Lines in  a  VMS makefile fall  into  four categories: target  lines,
X command lines, symbol lines, or directives.
X3 Dependencies
X Target lines must begin at column  one (no preceding whitespace). The
X colon separating the targets from the prerequisites  must be followed
X by  at  least one whitespace.   Targets and  prerequisites    may  be
X separated from each other by commas and/or whitespace. Because commas
X are simply replaced with whitespace in a target line, two commas in a
X row do not signify a null target. If  a target or  prerequisite has a
X qualifier attached  ('/junk'), the   qualifier  is removed  from  the
X module name. This allows a symbol to be used in both target lines and
X command lines.  For instance, you  may have  a symbol  defined as the
X list of LINK  options files, with each options  file  followed by the
X qualifier '/OPT'. This way, you can use the symbol in the target line
X AND the LINK command.
X3 Objects
X4 Normal files
X There are no tricks to specifying a normal file. Use the standard VMS
X syntax as described in the DCL Dictionary (et al.).
X4 Library modules
X A  name like 'a.olb(b)'  means the  module  named 'b'  stored in  the
X library named 'a.olb'.  This name is handled just like a normal  file
X name,  except  that a    special  routine  is entered   to  read  the
X modification  time  of the module in   the library.   If  the library
X doesn't exist, then it is made.  If there is a rule  for creating the
X library, such as:
X
X ###################
X a.olb :`09   #
X         lib/cre a #
X ###################
X
X then it will be created.  Otherwise, an error is  signalled since the
X library couldn't be found. The default  library type is '.OLB'  if no
X type is specified (this  is the VAX  Librarian's default).  Note that
X you can use a default rule to create the library if it doesn't exist:
X
X ##########################
X *.olb :`09`09  #
X         lib/cre $*`09  #
X *.hlb :`09`09  #
X         lib/help/cre $*  #
X *.tlb :`09`09  #
X         lib/cre/text $*  #
X ##########################
X4 CMS Library modules
X A  CMS module is specified as  the element name followed by   the '`7E'
X (tilde) character, optionally followed by a CMS library name. You can
X specify a default CMS  library using  the '%CMSLIB'  directive.   For
X example, the element FOO.C in library `5B.CMS`5D would be 'FOO.C`7E`5B.CMS`
V5D'.
X3 Default rules
X When  dealing with complex programs, it   is not  reasonable to  list
X explicitly rules for compilation of every module.   Rather, one would
X like to be able to say "If X.Obj is out of date, go look for X.C  and
X compile it, regardless of who X is." This  feature is provided by the
X default rules mechanism:
X
X ####################
X *.obj : *.c        # '$*' is a symbol which means
X         cc $*      # 'whatever matched the *'.
X ####################
X
X With the above makefile, we may compile any C program (FOO) by giving
X the  DCL command  '$ make foo.obj'.  The default  rule will match the
X '*' with 'foo' and will insert '$ cc foo' into the command file.
X
X The default rules will be searched for a pattern match only if either
X there are no rules for the file in question or there are rules but no
X command lines.  If a match is found, the default rules  will be added
X just  as  though they  were  found explicitly in  the makefile.   The
X symbol  '$*'  expands to the  root name (the  part   that matches the
X wildcard), '$@' expands to the name  of the target,  and '$<' expands
X to the name of the prerequisite.  In the case of multiple rules for a
X target, the first rule for  which  the  prerequisite _exists_ will be
X used.   If  no rule  has an  existing   prerequisite, then the  first
X matching rule will be used.
X
X The wildcard character '*' does not have to be the first character in
X the target. You  might  want to have  your  sources   and  objects in
X different directories, for example. The following rule epitomizes the
X flexibility of wildcards:
X
X       `5BOBJ`5D*.obj: `5BSRC`5D*.c
X            cc $</obj=$@
X
X Default rules  are added to  the head of  the internal rules list, so
X that a default  rule in the   Makefile will take precedence over   an
X identical rule in the default rules file.
X3 Command lines
X A  command line  is ANY  line  with  whitespace in  column one.   The
X commands apply to ALL targets on the most recent target line.  If the
X the last non-whitespace character on the command  line is a '$',  the
X dollar sign is removed and no  '$ ' will be  prepended to the command
X line on output. This allows you to indicate that  a 'command line' is
X actually a data line for a program and not a DCL command  line.  If a
X command line  ends  in '-', the following line  will be  handled as a
X continuation  line, so no  '$' will be  prepended to the _next_ line.
X If both the '$' and the '-' need to be used, such as when you wish to
X continue a data line, the order of the two characters IS significant.
X When using both special characters, the order should be  '-$',  since
X the dollar sign is removed first. If '$-' was  used, the '-' would be
X detected as the last character and the '$' would  be left at  the end
X of the line without having been processed.
X3 Symbol lines
X Symbol  definitions begin  in column  one. The  presence of  the  '='
X character  distinguishes  these lines from target lines.  The  symbol
X name is the first string  on the  left side of  the '='. The symbol's
X value is everything  to the right  of  the '=',  not   including  any
X whitespace following the '='. For instance, the  symbol line "FOO BAR
X JOE'S GARAGE  = This string" would  result in the symbol 'FOO'  being
X assigned the value "This string".  Note that since the delimiter  for
X the symbol name is ONLY whitespace, you can inadvertently have symbol
X names with special  characters   such   as  punctuation   or  control
X characters.  WARNING: if you define the one-character  symbols '`7B' or
X '(', you will be  unable to use multi-character   symbols.  Also, you
X must not define a multi-character  symbol with the characters  ')' or
X '`7D'  in them.  The  symbol  parsing routine is  not  smart  enough to
X understand that these are not symbol name terminators.
X3 Directives
X Directives are signified by a  '%'  in column  one. There  can not be
X whitespace between  the '%' and the  directive  name. Some directives
X take  arguments,  which must  be separated by   whitespace   from the
X directive name.
X4 INCLUDE <file>
X Causes the parsing routine to digest the specified file.
X4 CMSLIB <directory>
X Selects the specified directory as the default CMS library.
X4 CMSGEN <generation>
X Specifies the generation to be used for determining element dates.
X4 DEBUG
X Turn on debugging (similar to the /DEBUG command qualifier).
X4 NODEBUG
X Turn off debugging (similar to the /NODEBUG command qualifier).
X4 SPAWN
X Direct MAKE to spawn the command file at the end of execution.
X4 NOSPAWN
X Direct MAKE to execute the command file in the current process at the
X end of execution.
X3 Example
X # <== This is a comment indicator (in column one)
X ! <== As is this
X %SPAWN`09  !Force the command file to be spawned at the end of making
X foo.exe: foo.obj        # This is a target line`20
X         link foo        # This is a DCL command line`20
X
X foo.obj: foo.c foo.h    ! <== This is also a comment indicator`20
X         cc/lis foo`20
X
X As a human would read the description, it says:
X
X "FOO.EXE depends upon FOO.OBJ.  If FOO.EXE is out  of date or FOO.OBJ
X has been  'made', then  add  the command 'LINK   FOO' to   a file  of
X commands to be executed.  FOO.OBJ depends  upon FOO.C  and FOO.H.  If
X it is out of date, or either of its dependents have been 'made', then
X add 'CC/LIS FOO' to the command file to be executed."
X2 Makefiles
X MAKE tries to  locate two description files. The  first  is nominally
X the default rules file,  while the second is  the 'main'  description
X file. In  reality, there is no internal  distinction  between the two
X files.   No error is  reported  if neither file   is found.  An error
X during the MAKE phase will be displayed:  if you  specify a target on
X the command line and  it  exists, nothing  happens; if the  target is
X missing, the error  is "target  doesn't  exist and  couldn't be made"
X since there are no  rules; if no target  was specified on the command
X line, then MAKE has no target and complains accordingly.
X3 Default rules
X The first file which MAKE attempts to locate is a description of  the
X default rules to be applied when  no explicit rules for  a target are
X provided. Two  attempts   are made to locate  a  default rules  file.
X First,  the file "MAKE_DEFAULTS"  (which may be a  logical  name)  is
X tried.   If the open  fails on  this file, MAKE   tries for the  file
X "MAKE.INI".
X3 Makefile
X The  second file which MAKE  attempts to locate  is "MAKEFILE", which
X may be a logical name. You may also specify this file on the  command
X line via the '/INPUT' qualifier.
X2 General_Advice
X The Makefile that comes with MAKE/VMS is a very good  example of what
X can be done with the system.  Also, UNIX makefiles  are fairly useful
X to examine, but obviously the command lines will be different.  Also,
X since it is horribly inefficient  in VMS to spawn  a subprocess   for
X each command, there is a fundamental difference between the operation
X on VMS and UNIX.  UNIX MAKE spins off a new process for each command,
X but MAKE/VMS builds  a command file  and defers  execution until  the
X end.  I like  my way    much better, but  I  have  the  advantage  of
X hindsight.  Of  course, there are   some significant drawbacks to the
X batch method, too.
X2 Bugs
X There are always bugs. I'm sure there are plenty.  Report bugs to
X
X      Todd Aven
X      The Software Sweatshop
X      564 Laurelton Blvd.
X      Long Beach, NY  11561
X      (516)432-3002
X
X      net address: todd@cincom.umd.edu
X      ******** Please include /DEBUG output. *********
$ CALL UNPACK MAKE.HLP;23 1834368602
$ create 'f'
X*.obj: *.cld
X`09set command $</object=$@
X
X*.obj: *.mar
X`09macro $</obj=$@
X
X*.obj: *.c
X`09cc'cflags' $</obj=$@
X
X*.olb:
X`09lib/cre $*
X
X*.mft: *.pck
X`09mftu encode $</out=$@
X*.pck: *.bck
X`09mftu pack $</out=$@
X
X*.dir:
X`09cre/dir `5B.$*`5D
$ CALL UNPACK MAKE.INI;17 1477929134
$ create 'f'
Xident="v3.4"
Xsys$share:vaxcrtl/share
$ CALL UNPACK MAKE.OPT;5 286658967
$ create 'f'
XModule MAKECMDTABLE
X
Xdefine verb MAKE
X parameter P1
X parameter P2
X parameter P3
X parameter P4
X parameter P5
X parameter P6
X parameter P7
X parameter P8
X qualifier INPUT`09nonnegatable,
X`09`09`09default,
X`09`09`09value(default="MAKEFILE",type=$infile)
X qualifier OUTPUT`09nonnegatable,
X`09`09`09value(required,type=$outfile)
X qualifier SPAWN
X qualifier VERIFY
X qualifier IGNORE
X qualifier EXECUTE`09default
X qualifier DEBUG
X qualifier KEEP
X qualifier DEFAULTS`09default
X qualifier FORCE
X qualifier HELP
$ CALL UNPACK MAKECMD.CLD;19 1684429482
$ create 'f'
XSYSDIR = sys_util:`5B000000`5D`09`09# make system will put things here.
XDISTDIR = `5B.dist`5D
X
XLINK = link'lflags'`09`09# if debugging, lflags will be /debug.
X
XOBJS = make.obj,-
X`09mstring.obj,-
X`09cmdline.obj,-
X`09makecmd.obj,-
X`09dates.obj,-
X`09make.opt/opt
X
Xmake: make.exe make.hlb(make) # This is the executable.
X
Xsystem: $`7BSYSDIR`7Dmake.exe $`7BSYSDIR`7Dmake.ini
X
X# the backslash '\' is the continuation character if it occurs as
X# the last non-white character on the line.
X# the hyphen '-' is the DCL continuation character, so if it occurs
X# as the last non-white character on the line, the next line will
X# not have the dollar sign '$' prepended.
X
Xmake.exe: $`7BOBJS`7D
X`09$`7BLINK`7D $`7BOBJS`7D
X
Xmake.hlb(make): make.hlp
X`09lib/help make make
X
X*.hlb:
X`09lib/cre/help $*
X
Xmake.obj: make.h functions.h cmdline.h strings.h simple.h clidef.h
Xmstring.obj: make.h functions.h simple.h
Xcmdline.obj: cmdline.h strings.h
Xdates.obj: simple.h strings.h cmdline.h
X
Xdebug:
X`09cflags := /debug/noopt
X`09lflags := /debug
X
X$`7BDISTDIR`7Dmake.exe: make.exe
X`09copy make.exe $`7BDISTDIR`7D/log
X
X$`7BSYSDIR`7Dmake.exe: make.exe
+-+-+-+-+-+-+-+-  END  OF PART 5 +-+-+-+-+-+-+-+-
-+-+-+-+-+-+-+-+ START OF PART 6 -+-+-+-+-+-+-+-+
X`09copy make.exe $`7BSYSDIR`7D/log
X$`7BSYSDIR`7Dmake.ini: make.ini
X`09copy make.ini $`7BSYSDIR`7D/log
X
Xclean:
X`09purge/log
X`09purge/log $`7BDISTDIR`7D
X
Xtest_$:`09# the dollar sign at the end of the line
X`09# means don't put a dollar sign at the beginning of the line in the
X`09# command file.
X type sys$input:
X`09Now is the time for all good men to come to the aid of their $ #test
X`09countrymen. $ !test
X
Xlib: make.olb(make)
X
Xmake.olb(make): make.obj
X`09lib make make
X
XEXAMPLES = makefile.,make.ini
XDOCS = release.v34,make.hlp
XSOURCES = make.c,mstring.c,cmdline.c,dates.c,makecmd.cld,-
X`09make.h,functions.h,cmdline.h,strings.h,simple.h,clidef.h,-
X`09make.opt
XDISTLIST =  $`7BDOCS`7D,$`7BEXAMPLES`7D,make.exe,$`7BSOURCES`7D
X
Xdist: dist.dir $`7BDISTDIR`7Dmake.bck
X`09purge/log $`7BDISTDIR`7D
X
X$`7BDISTDIR`7Dmake.bck: $`7BDISTLIST`7D
X`09purge/log
X`09backup/list/buffer=5 $`7BDISTLIST`7D $`7BDISTDIR`7Dmake.bck/save
$ CALL UNPACK MAKEFILE.;5 1863752316
$ create 'f'
X/* mstring.c */
X
X/* The purpose of this file is to provide subroutines for handling
X   strings whose space is allocated with malloc - in this way we remove
X   all limitations on length of strings */
X
X#include <ctype.h>
X#include <stdio.h>
X
X#include "simple.h"
X#include "make.h"
X#include "functions.h"
X
X#define tfree(x) if (x) free(x),x=NULL
X#define LLEN 256`09`09/* Max file line length */
X#define MLEN 10*LLEN`09`09/* Max multi-line length */
X
X/************************************************************/
X/* getline
X *
X *`09Get a line from stream.
X *`09Remove comments.
X *`09See if there's a continuation mark ('\' or '-').
X *`09Get rid of trailing spaces.
X *`09Repeat if there was a continuation mark.
X *
X */
Xchar *getline(FILE *stream)
X`7B
X  char *ptr, *comment;
X  static char output`5BMLEN`5D;
X  char buf`5BLLEN`5D;
X  int continued_line = TRUE;
X
X  if(feof(stream))
X    return(NULL);
X  output`5B0`5D = '\0';`09`09/* The final result will be here. */
X  while(continued_line) `7B
X    buf`5B0`5D = '\0';`09`09/* Each physical line goes here. */
X    fgets(buf,LLEN,stream);
X    if(strlen(buf) == 0)
X      break;`09`09`09/* All done. */
X    /* Truncate at the leftmost comment character. */
X    if(comment = strchr(buf,'#')) /* Either '#' or '!' or both. */
X      *comment = '\0';
X    if(comment = strchr(buf,'!'))
X      *comment = '\0';
X    /* See if it's a continuation line. */
X    ptr = lastnonspace(buf);
X    if(*ptr == '\\' `7C`7C *ptr == '-') `7B
X      *ptr = ' ';`09`09/* Change to space to trim space off end */
X      continued_line = TRUE;
X    `7D
X    else `7B
X      continued_line = FALSE;
X    `7D
X
X    /* Re-evaluate the last non-space, if we changed the line. */
X    if(continued_line)
X      ptr = lastnonspace(buf);
X
X    switch(*ptr) `7B
X    case '\0':
X      break;`09`09`09/* Don't gotta do nuthin' */
X    case ' ':
X    case '\t':
X    case '\n':
X      *ptr = '\0';
X      break;
X    default:
X      *(ptr+1) = '\0';`09`09/* Everything after is whitespace */
X      break;
X    `7D
X
X    if((buf`5B0`5D == '\0') && !continued_line)
X      break;`09`09`09/* All done. Return what we have already. */
X
X    /* Put the continuation character back in to be replaced at excretion */
X    if(continued_line)
X      strcat(buf,"\\");
X
X    /* Move the current line to the output buffer */
X    if(strlen(output)+strlen(buf) < MLEN)
X      strcat(output,buf);
X    else
X      error("Continued line too long");
X  `7D`20
X  return(strperm(output));
X`7D
X/************************************************************/
X/* lastnonspace
X *
X *`09Returns ptr to the last character on a line that isn't whitespace.
X */
Xchar *lastnonspace(char *line)
X`7B
X  char *ptr;
X  int len;
X `20
X  if((len = strlen(line)) == 0) return(line);
X  ptr = line + strlen(line) - 1; /* Point to last character */
X  while(isspace(*ptr)) `7B
X    if(ptr == line) break;
X    ptr--;
X  `7D
X  return(ptr);
X`7D
X/************************************************************/
Xchar *mstrcat(char *p, char *q)
X`7B
X  char *r = talloc (strlen(p) + strlen(q) + 1);
X  strcpy(r,p);
X  strcat(r,q);
X  return r;
X`7D
X/************************************************************/
X/* allocate space for s, return new pointer */
Xchar *strperm(char *s)
X`7B
X  char *t = talloc(strlen(s)+1);
X  if (s==NULL) error ("strperm - NULL pointer");
X  strcpy(t,s);
X  return t;
X`7D
X/************************************************************/
Xchar passpace(char **p)
X`7B
X  if (*p==NULL) error("NULL arg to passpace");
X  while (isspace (**p)) (*p)++;
X  return(**p);
X`7D
X/************************************************************/
Xchar passnonsp(char **p)
X`7B
X  if (p==NULL  `7C`7C  *p==NULL) error("NULL arg to passnonsp");
X  while (**p && !isspace(**p)) (*p)++;
X  return **p;
X`7D
X/************************************************************/
Xvoid error(char *errmsg)
X`7B
X  extern char *cmd_fname;
X
X  fputs("%MAKE, ", stderr);
X  fputs(errmsg,stderr);
X  if(cmd_fname)delete(cmd_fname);
X  exit(2);
X`7D
X/************************************************************/
Xchar *talloc(int i)
X`7B
X  char *p;
X  char *malloc();
X  p = malloc(i);
X  if (p==NULL) error ("no more memory");
X  return p;
X`7D
X/************************************************************/
Xchar *findnonwhite(char *string)
X`7B
X  int pos = strspn(string,WHITESPACE);
X  if(pos == strlen(string))
X    return(NULL);
X  else
X    return(string+pos);
X`7D
X/************************************************************/
Xchar *findwhite(char *string)
X`7B
X  int pos = strcspn(string,WHITESPACE);
X  if(pos == strlen(string))
X    return(NULL);
X  else
X    return(string+pos);
X`7D
$ CALL UNPACK MSTRING.C;1 696217010
$ create 'f'
X========================================================================
XVersion 3.4 Notes
X========================================================================
Xo Repaired some misfeatures in the CMS interface
X`09- The symbol 'CMS' is no longer defined.
X`09- If the %CMSGEN directive is encountered, the symbol 'CMSGEN' is
X`09  defined as "/GEN=<gen>". Otherwise, it is an empty string, but
X`09  is still available as a symbol.
X`09- If the %CMSLIB directive is encountered, the symbol 'CMSLIB' is
X`09  defined to have the same value as the %CMSLIB directive. Otherwise,
X`09  it is UNDEFINED.
Xo The help file was updated.
X
X========================================================================
XVersion 3.3 Notes
X========================================================================
X
Xo Dynamic data structures
X
X    The data structures are almost all dynamically allocated, to allow
X    large makefiles to   be  interned.  The  only significant   static
X    structures  are the   input  buffer,  which  is limited  to    256
X    characters by default, and the input  line expansion buffer, which
X    is limited to 2560 characters.
X
Xo Generalized wildcarding in default rules
X
X    The '*' in a default rule does not have to  be the first character
X    in the  rule any more.  Only  one asterisk  may be  present in the
X    rule, but it may occur anywhere in the string and will be properly
X    replaced by the root matching name at runtime.
X
X========================================================================
XVersion 3.2 Notes
X========================================================================
X
X0. Data Structures
X------------------
X`09The  data  structures were  re-written so that  (hopefully)  a
Xsignificant improvement  in the speed  of  parsing and  making will be
Xobserved. Many  strings  that were   malloc'ed  on   the fly  are  now
Xcharacter arrays on the stack. I'm pretty sure this  is more efficient
Xthan lots of repetitive calls to malloc(). Where I was uncertain about
Xthe maximum length of a string,  I left it as  a dynamically allocated
Xstring.  The 'File' structure retains  more information about the file
Xso that it is unnecessary to  access the file  more  than once for any
Xreason.
X
X1. The Command Interface
X------------------------
X`09The command interface for MAKE uses DCL qualifiers  instead of
XUNIX switches.   MAKE/HELP   will  produce the  list    of  acceptable
Xqualifiers.
X
X2. Default Rules
X----------------
X`09Rules are added to the head of the default rules list, so that
Xif a redundant default rule is encountered it supersedes the old copy,
Xsince lists are searched  from  head to tail.   Multiple default rules
Xfor a given target are allowed.  This means that a .OBJ may depend  on
X.C OR .MAR OR  .CLD. Just list  each  rule separately. The  first rule
Xthat matches the  target AND has  an existing matching prerequisite is
X'made'. If no  matching rule  is found   that has a prerequisite  that
Xexists, the first rule (if any)  that matched the  target  is used. If
Xmore than one default rule matches, the LAST one in the makefile(s) is
Xused.
X
X3. Target Line syntax
X---------------------
X`09Target lines have a new extension to the syntax. The allowable
Xdelimiters   for targets and  prerequisites are   whitespace (old) and
Xcommas (new).  This allows one to define a symbol as a  list of object
Xmodules separated by commas,  so that the  symbol may be  used in both
Xthe target line and the link command. The colon separating the targets
Xfrom  the  prerequisites must  still  be   followed by    at least one
Xwhitespace character if there are any prerequisites.
X
X4. Symbol Definitions
X---------------------
X`09Additional definitions of a  symbol  supersede the  old value.
XAll whitespace  at  the beginning of  the symbol  definition is eaten.
XInternally,  superseding a symbol frees  the  old  value's storage and
Xallocates new storage for the new value.
X
X5. Continuation Lines
X---------------------
X`09Continuation lines are handled in  a much cleaner manner.  The
Xallowable continuation characters are the backslash '\' and the hyphen
X'-'. Upon digesting an input line, both are  converted to a backslash.
XWhen target lines are parsed, all backslashes are converted to spaces,
Xso it is important not to continue  a line in  the middle of a  target
Xname.   When  DCL  command  lines  are  added   to the  output   file,
Xbackslashes are converted to hyphens and the line is broken  after the
Xhyphen. If  a  line is both a  DCL data line  ('$' at the  end)  and a
Xcontinued  line ('-' at the  end),  the proper  syntax is '-$',  since
Xinternally the '$' is parsed and removed first.
X
X6. Directives
X-------------
X`09Directives are commands to MAKE embedded in the makefile which
Xgive  it certain information.  They  must  be ALL CAPS.  The directive
X'%INCLUDE' will cause MAKE to parse the specified file. '%CMSLIB' will
Xindicate to  MAKE the name of a  CMS library to use  as  a default  in
Xcases of missing specifications. Directives must begin  in  the  first
Xcolumn of a line.  This is the complete list of directives:
X %INCLUDE <filename>`09Read in and parse an additional makefile
X %CMSLIB <directory>`09Establish a default CMS library for CMS target
X %CMSGEN <generation>`09Select a specific generation
X %DEBUG`09`09`09Turn on debugging (like MAKE/DEBUG)
X %NODEBUG`09`09Turn off debugging
X %SPAWN`09`09`09Cause commands to spawn (like MAKE/SPAWN)
X %NOSPAWN`09`09Cause commands to execute in current process
X
X7. Specifying CMS Elements
X--------------------------
X`09The file specification  syntax has been augmented  to  support
XCMS  modules.     Following a normal   file  specification with a  '`7E'
Xindicates that the file is located  in  a CMS library.  Any characters
Xfollowing the tilde are interpreted as the  CMS library.  For example,
Xto specify module  FOO.C in CMS  library  FOO_DEV:`5BTEST`5D, the  correct
X'file  name'   would  be 'FOO.C`7EFOO_DEV:`5BTEST`5D'.   Alternatively,  the
X'%CMSLIB' directive can be used to set the default CMS library and the
X'file    name'  would be  'FOO.C`7E'   (the directive would be  '%CMSLIB
XFOO_DEV:`5BTEST`5D').  If no %CMSGEN directive is encountered, the highest
Xgeneration of the element(s) will be  examined. The symbol '$`7BCMS`7D' is
XALWAYS available  to  the  user.  If no  %CMSGEN directive   has  been
Xencountered, the 'CMS'  symbol value is  "CMS". If you define  a   CMS
Xgeneration, the symbol  'CMS' is  set  to "CMS/GEN=<generation>" where
X<generation> is the value from the %CMSGEN directive.
X
X8. Filenames With Qualifiers Attached
X-------------------------------------
X`09It seems to happen  often  that I'd  like  to have  a   symbol
Xdefined as the list of files  linked into my  executable, and I'd like
Xto use the symbol   in   the target  line and  the link command.  With
Xoptions  files and object  libraries you must  have a qualifier in the
Xlink command to denote the special  nature of those files,  but if you
Xput the qualifier on  the name  of the file in  the symbol definition,
Xthe  target  parsing would  choke.  Well,   by golly I fixed it!    In
Xparse_target_line(), I discard  "/"  and  anything following it   in a
Xfilename. For example, the makefile might look like this:
X
X`09OBJS = foo.obj,foo.opt/opt
X`09foo.exe: $`7BOBJS`7D
X`09`09link $`7BOBJS`7D
X
X
$ CALL UNPACK RELEASE.V34;1 841279912
$ create 'f'
X#define TRUE 1
X#define FALSE 0
X#define AND &&
X#define NOT !
X#define OR `7C`7C
X
X#define tfree(x) if(x) free(x),x=NULL
X
X#define Dprintf if(debug)printf
X
$ CALL UNPACK SIMPLE.H;1 289554352
$ create 'f'
X
X#define strdesc`09`09struct dsc$descriptor_s
X#define DESCRIP(X,Y)`09X.dsc$w_length = strlen(Y), \
X`09`09`09X.dsc$b_dtype = DSC$K_DTYPE_T, \
X`09`09`09X.dsc$b_class = DSC$K_CLASS_S, \
X`09`09`09X.dsc$a_pointer = Y;
X
X#define varstr(MAXLEN)`09struct `7Bunsigned short curlen; char body`5BMAXLEN
V`5D;`7D
X#define vardesc`09`09struct dsc$descriptor_vsa
X#define VDESCRIP(X,Y)`09X.dsc$w_maxstrlen = sizeof(Y) - 3, \
X`09`09`09X.dsc$b_dtype = DSC$K_DTYPE_VT, \
X`09`09`09X.dsc$b_class = DSC$K_CLASS_VS, \
X`09`09`09X.dsc$a_pointer = &Y;
X
X#define UPCASE(X,Y)`09for(Y=X; *Y!='\0'; Y++) *Y = toupper(*Y);
X#define DOWNCASE(X,Y)`09for(Y=X; *Y!='\0'; Y++) *Y = tolower(*Y);
X
X#define STRING(X,Y)`09X = malloc(strlen(Y)+1), strcpy(X,Y);
$ CALL UNPACK STRINGS.H;1 1193607490
$ v=f$verify(v)
$ EXIT
