/* RCS  $Id: stat.c,v 1.3 2007-10-15 15:41:38 ihi Exp $
--
-- SYNOPSIS
--      Bind a target name to a file.
--
-- DESCRIPTION
--  This file contains the code to go and stat a target.  The stat rules
--  follow a predefined order defined in the comment for Stat_target.
--
-- AUTHOR
--      Dennis Vadura, dvadura@dmake.wticorp.com
--
-- WWW
--      http://dmake.wticorp.com/
--
-- COPYRIGHT
--      Copyright (c) 1996,1997 by WTI Corp.  All rights reserved.
--
--      This program is NOT free software; you can redistribute it and/or
--      modify it under the terms of the Software License Agreement Provided
--      in the file <distribution-root>/readme/license.txt.
--
-- LOG
--      Use cvs log to obtain detailed change logs.
*/

#include "extern.h"


static  int _check_dir_list ANSI((CELLPTR, CELLPTR, int, int));

#ifdef DBUG
   /* Just a little ditty for debugging this thing */
   static time_t
   _do_stat( name, lib, sym, force )
   char *name;
   char *lib;
   char **sym;
   int  force;
   {
      time_t res;
      DB_ENTER( "_do_stat" );

      res = Do_stat(name, lib, sym, force);
      DB_PRINT( "stat", ("Statted [%s,%s,%d,%ld]", name, lib, sym, res) );

      DB_RETURN( res );
   }
#define DO_STAT(A,B,C,D)  _do_stat(A,B,C,D)
#else
#define DO_STAT(A,B,C,D)  Do_stat(A,B,C,D)
#endif

static char *_first;    /* If set this variable saves the first pathname that was
             * used to stat the target in, if subsequently a match is
             * found it is overridden by the matched path name. */

PUBLIC void
Stat_target( cp, setfname, force )/*
====================================
    Stat a target.  When doing so follow the following rules, suppose
    that cp->CE_NAME points at a target called fred.o:
    (See also man page: BINDING TARGETS)

        0.      If A_SYMBOL attribute set look into the library
            then do the steps 1 thru 4 on the resulting name.
        1.  Try path's obtained by prepending any dirs found as
            prerequisites for .SOURCE.o.
        2.  If not found, do same as 2 but use .SOURCE
            The predefined '.SOURCE  : .NULL' targets takes care
            of local/absolute paths.
        3.  If not found and .LIBRARYM attribute for the target is
            set then look for it in the corresponding library.
            4.  If found in step 0 thru 3, then ce_fname points at
            file name associate with target, else ce_fname points
            at a file name built by the first .SOURCE* dir that
            applied.
    If setfname is != 0 this tells _check_dir_list() to set the static
    _first variable. setfname also controls the use of _first.
    If it is -1 ce_fname (the file name associated with target) is only
    set if a matching file was found and statted, if it is 1 ce_fname
    is set to _first even if target doesn't exist yet.

    If force is TRUE really stat the target. Do not use the directory
    cache but update the files entry if it's enabled. */

CELLPTR cp;
int     setfname;
int     force;
{
   register HASHPTR hp;
   static   HASHPTR srchp = NIL(HASH);
   char         *name;
   char         *tmp;
   int          res = 0;

   DB_ENTER( "Stat_target" );

   name = cp->CE_NAME;
   DB_PRINT( "stat", ("called on [%s]", name) );

   if( srchp == NIL(HASH) ) srchp = Get_name(".SOURCE",Defs,FALSE);

   /* Look for a symbol of the form lib((symbol)) the name of the symbol
    * as entered in the hash table is (symbol) so pull out symbol and try
    * to find it's module.  If successful DO_STAT will return the module
    * as well as the archive member name (pointed at by tmp).  We then
    * replace the symbol name with the archive member name so that we
    * have the proper name for any future refrences. */

   if( cp->ce_attr & A_SYMBOL ) {
      DB_PRINT( "stat", ("Binding lib symbol [%s]", name) );

      cp->ce_time = DO_STAT( name, cp->ce_lib, &tmp, force );

      if( cp->ce_time != (time_t) 0L ) {
     /* stat the new member name below  note tmp must point at a string
      * returned by MALLOC... ie. the Do_stat code should use DmStrDup */

     if( Verbose & V_MAKE )
        printf( "%s:  Mapped ((%s)) to %s(%s)\n", Pname,
             name, cp->ce_lib, tmp );

         FREE( name );
     name = cp->CE_NAME = tmp;
     cp->ce_attr &= ~(A_FFNAME | A_SYMBOL);
      }
      else
         { DB_VOID_RETURN; }
   }

   _first = NIL(char);
   tmp = DmStrJoin( ".SOURCE", Get_suffix(name), -1, FALSE);

   /* Check .SOURCE.xxx target */
   if( (hp = Get_name(tmp, Defs, FALSE)) != NIL(HASH) )
      res = _check_dir_list( cp, hp->CP_OWNR, setfname, force );

   /* Check just .SOURCE */
   if( !res && (srchp != NIL(HASH)) )
      res = _check_dir_list( cp, srchp->CP_OWNR, setfname, force );

   /* If libmember and we haven't found it check the library */
   if( !res && (cp->ce_attr & A_LIBRARYM) ) {
      cp->ce_time = DO_STAT(name, cp->ce_lib, NIL(char *), force);

      if( !cp->ce_time && Tmd && *Tmd && cp->ce_lib ) {
     char *tmplib;
     tmplib=DmStrDup(Build_path(Tmd,cp->ce_lib));

     if ((cp->ce_time = DO_STAT(name, tmplib, NIL(char *),force)) != (time_t)0L){
        cp->ce_lib=DmStrDup(tmplib);
     }
      }

      if( Verbose & V_MAKE )
     printf( "%s:  Checking library '%s' for member [%s], time %ld\n",
         Pname, cp->ce_lib, name, cp->ce_time );
   }

   FREE( tmp );

   if( setfname == 1 || (setfname == -1 && cp->ce_time != (time_t)0L) ) {
      int setlib = (cp->ce_lib == cp->ce_fname);

      if( (cp->ce_attr & A_FFNAME) && (cp->ce_fname != NIL(char)) )
     FREE( cp->ce_fname );

      if( _first != NIL(char) ) {
     cp->ce_fname = _first;
     cp->ce_attr |= A_FFNAME;
      }
      else {
     cp->ce_fname = cp->CE_NAME;
     cp->ce_attr &= ~A_FFNAME;
      }

      if ( setlib ) cp->ce_lib = cp->ce_fname;
   }
   else if( _first )
      FREE( _first );

   /* set it as stated only if successful, this way, we shall try again
    * later. */
   if( cp->ce_time != (time_t)0L ) {
      cp->ce_flag |= F_STAT;

      /* If it is a whatif this changed scenario then return the current
       * time, but do so only if the stat was successful. */
      if ( (cp->ce_attr & A_WHATIF) && !(cp->ce_flag & F_MADE) ) {
     cp->ce_time = Do_time();
      }
   }

   DB_VOID_RETURN;
}


static int
_check_dir_list( cp, sp, setfname, force )/*
============================================
    Check the list of dir's given by the prerequisite list of sp, for a
    file pointed at by cp.  Returns 0 if path not bound, else returns
    1 and replaces old name for cell with new cell name. */

CELLPTR cp;
CELLPTR sp;
int     setfname;
int     force;
{
   /* FIXME: BCC 5.0 BUG??? If lp is assigned to a register variable then
    *        BCC 5.0 corrupts a field of the member structure when DO_STAT
    *        calls the native win95 stat system call. Blech!!!
    *
    *        Making this a static variable forces it out of a register and
    *        seems to avoid the problem. */
   static LINKPTR lp;
   char *dir;
   char *path;
   char *name;
   int  res  = 0;
   int  fset = 0;

   DB_ENTER( "_check_dir_list" );
   DB_PRINT( "mem", ("%s:-> mem %ld", cp->CE_NAME, (long) coreleft()) );

   if( sp->ce_prq != NIL(LINK) )    /* check prerequisites if any */
   {
      /* Use the real name instead of basename, this prevents silly
       * loops in inference code, and is consistent with man page */
      name = cp->CE_NAME;

      /* Here we loop through each directory on the list, and try to stat
       * the target.  We always save the first pathname we try to stat in
       * _first.  If we subsequently get a match we then replace the value of
       * _first by the matched path name.  */

      for( lp=sp->CE_PRQ; lp != NIL(LINK) && !res; lp=lp->cl_next ) {
     int  nodup = 0;
     dir  = lp->cl_prq->CE_NAME;

     if( strchr( dir, '$' ) ) dir = Expand(dir);
     if( strcmp( dir, ".NULL" ) == 0 ) {
        nodup = 1;
        path = cp->CE_NAME;
     }   else {
        path = DmStrDup(Build_path(dir,name));
     }

     res = ((cp->ce_time=DO_STAT(path,NIL(char),NIL(char *),force))!=(time_t)0L);

     /* Have to use DmStrDup to set _first since Build_path, builds it's
      * path names inside a static buffer. */
     if( setfname )
        if( (_first == NIL(char) && !fset) || res ) {
           if( _first != NIL(char) ) FREE( _first );
           if (nodup)
          _first = NIL(char);
           else {
          _first = path;
          path = NIL(char);
           }
           fset = 1;
        }

     DB_PRINT( "stat", ("_first [%s], path [%s]", _first, path) );
     if( dir != lp->cl_prq->CE_NAME )  FREE(dir);
     if( path && path != cp->CE_NAME ) FREE(path);
      }
   }

   DB_PRINT( "mem", ("%s:-< mem %ld", cp->CE_NAME, (long) coreleft()) );
   DB_RETURN( res );
}




