/*
 * Copyright 1993, 2000 Christopher Seiwald.
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */

/*  This file is ALSO:
 *  (C) Copyright David Abrahams 2001. Permission to copy, use,
 *  modify, sell and distribute this software is granted provided this
 *  copyright notice appears in all copies. This software is provided
 *  "as is" without express or implied warranty, and with no claim as
 *  to its suitability for any purpose.
 */

# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "compile.h"
# include "rules.h"
# include "variable.h"
# include "regexp.h"
# include "hdrmacro.h"
# include "hash.h"
# include "newstr.h"
# include "strings.h"

/*
 * hdrmacro.c - handle header files that define macros used in
 *              #include statements.
 *
 *  we look for lines like "#define MACRO  <....>" or '#define MACRO  "    "'
 *  in the target file. When found, we 
 *
 *  we then phony up a rule invocation like:
 *
 *	$(HDRRULE) <target> : <resolved included files> ;
 *
 * External routines:
 *    headers1() - scan a target for "#include MACRO" lines and try
 *                 to resolve them when needed
 *
 * Internal routines:
 *    headers1() - using regexp, scan a file and build include LIST
 *
 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
 * 09/10/00 (seiwald) - replaced call to compile_rule with evaluate_rule,
 *		so that headers() doesn't have to mock up a parse structure
 *		just to invoke a rule.
 */

static LIST *header_macros1( LIST *l, char *file, int rec, regexp *re[] );

/* this type is used to store a dictionary of file header macros */
typedef struct header_macro
{
  char*  symbol;
  char*  filename;  /* we could maybe use a LIST here ?? */
  
} HEADER_MACRO;
 
static struct hash*   header_macros_hash = 0;

/*
 * headers() - scan a target for include files and call HDRRULE
 */

# define MAXINC 10

void
macro_headers( TARGET *t )
{
    static regexp *re = 0;
    FILE	*f;
    char	buf[ 1024 ];
    int	i;
    
    if ( DEBUG_HEADER )
        printf( "macro header scan for %s\n", t->name );

    /* this regexp is used to detect lines of the form       */
    /* "#define  MACRO  <....>" or "#define  MACRO  "....."  */
    /* in the header macro files..                           */
    if ( re == 0 )
    {
        re = regex_compile(
            "^[ 	]*#[ 	]*define[ 	]*([A-Za-z][A-Za-z0-9_]*)[ 	]*"
            "[<\"]([^\">]*)[\">].*$" );
    }
    
    if( !( f = fopen( t->boundname, "r" ) ) )
        return;

    while( fgets( buf, sizeof( buf ), f ) )
    {
        HEADER_MACRO  var, *v = &var;

        if ( regexec( re, buf ) && re->startp[1] )
        {
            /* we detected a line that looks like "#define  MACRO  filename */
            re->endp[1][0] = '\0';
            re->endp[2][0] = '\0';
        
            if ( DEBUG_HEADER )
                printf( "macro '%s' used to define filename '%s' in '%s'\n",
                        re->startp[1], re->startp[2], t->boundname );

            /* add macro definition to hash table */
            if ( !header_macros_hash )
                header_macros_hash = hashinit( sizeof( HEADER_MACRO ), "hdrmacros" );

            v->symbol   = re->startp[1];
            v->filename = 0;
            if ( hashenter( header_macros_hash, (HASHDATA **)&v ) )
            {
                v->symbol   = newstr( re->startp[1] );  /* never freed */
                v->filename = newstr( re->startp[2] );  /* never freed */
            }
            /* XXXX: FOR NOW, WE IGNORE MULTIPLE MACRO DEFINITIONS !! */
            /*       WE MIGHT AS WELL USE A LIST TO STORE THEM..      */
        }
    }

    fclose( f );
}


char*
macro_header_get( const char*  macro_name )
{
  HEADER_MACRO  var, *v = &var;

  v->symbol = (char*)macro_name;

  if( header_macros_hash && hashcheck( header_macros_hash, (HASHDATA **)&v ) )
  {
    if ( DEBUG_HEADER )
      printf( "### macro '%s' evaluated to '%s'\n", macro_name, v->filename );
    return v->filename;
  }
  return 0;  
}

