/* $RCSfile: parse.c,v $
-- $Revision: 1.8 $
-- last change: $Author: ihi $ $Date: 2007-10-15 15:40:45 $
--
-- SYNOPSIS
--      Parse the input, and perform semantic analysis
--
-- DESCRIPTION
--  This file contains the routines that parse the input makefile and
--  call the appropriate routines to perform the semantic analysis and
--  build the internal dag.
--
-- 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"


PUBLIC void
Parse( fil )/*
==============  Parse the makefile input */
FILE *fil;
{
  int  rule  = FALSE;                 /* have seen a recipe line        */
  char *p;                 /* termporary pointer into Buffer */
  char *pTmpBuf;

  DB_ENTER( "Parse" );

  State = NORMAL_SCAN;
  Group = FALSE;                 /* true if scanning a group rcpe  */
  while( TRUE ) {
    if( Get_line( Buffer, fil ) ) {
      if( Group )  Fatal( "Incomplete rule recipe group detected" );

      /* If we are still in RULE_SCAN mode there might be unbound recipes.  */
      if( State == RULE_SCAN )
        Bind_rules_to_targets( F_DEFAULT );

      if( fil != NIL( FILE ) )               /* end of parsable input */
        Closefile();

      DB_VOID_RETURN;
    }
    else {

#ifdef _MPW
      if ( Buffer[0] == 10 )
        pTmpBuf = Buffer+1;
      else
#endif
        pTmpBuf = Buffer;

#ifdef _MPW
      p = pTmpBuf;
      while ( *p )
        {
          if ( *p == 10 )
            *p = '\t';
          p++;
        }
#endif

      switch( State ) {
      case RULE_SCAN:

        /* Check for the `[' that starts off a group recipe definition.
         * It must appear as the first non-white space
         * character in the line. */

        p = DmStrSpn( Buffer, " \t\r\n" );
        if( Set_group_attributes( p ) ) {
          if( Group )
            Fatal( "New group recipe begin found within group recipe." );
          else if( rule )
            Fatal( "Cannot mix single and group recipe lines." );
          else
            Group = TRUE;

          rule = TRUE;

          break;                     /* ignore the group start  */
        }

        if( Group ) {
          if( *p != ']' ) {
            Add_recipe_to_list( pTmpBuf, TRUE, TRUE );
            rule = TRUE;
          }
          else
            State = NORMAL_SCAN;
        }
        else {
          if(    *pTmpBuf == '\t'
                 || (Notabs && *pTmpBuf == ' ') ) {
            Add_recipe_to_list( pTmpBuf, FALSE, FALSE );
            rule = TRUE;
          }
          else if( *p == ']' )
            Fatal( "Found unmatched ']'" );
          else if( *pTmpBuf ) /* Something that was no recipe. */
            State = NORMAL_SCAN;
          /* The only thing that was not handled was an empty line. */
        }

        if( State == RULE_SCAN ) break;     /* ie. keep going    */

        Bind_rules_to_targets( (Group) ? F_GROUP: F_DEFAULT );

        rule = FALSE;
        if( Group ) {
          Group = FALSE;
          break;
        }
        /*FALLTRHOUGH*/

        /* In this case we broke out of the rule scan because we do not
         * have a recipe line that begins with a <TAB>, so lets
         * try to scan the thing as a macro or rule definition. */


      case NORMAL_SCAN:
        if( !*pTmpBuf ) continue;         /* we have null input line */

        /* STUPID AUGMAKE uses "include" at the start of a line as
         * a signal to include a new file, so let's look for it.
         * if we see it replace it by .INCLUDE: and stick this back
         * into the buffer. */
        if( !strncmp( "include", pTmpBuf, 7 ) &&
            (pTmpBuf[7] == ' ' || pTmpBuf[7] == '\t') )
          {
            char *tmp;

            tmp = DmStrJoin( ".INCLUDE:", pTmpBuf+7, -1, FALSE );
            strcpy( pTmpBuf, tmp );
            FREE( tmp );
          }

        /* look for a macro definition, they all contain an = sign
         * if we fail to recognize it as a legal macro op then try to
         * parse the same line as a rule definition, it's one or the
         * other */

        if( Parse_macro(pTmpBuf, M_DEFAULT) ) break;/* it's a macro def*/
        if( Parse_rule_def( &State ) )      break;/* it's a rule def */

        /* if it is an empty or blank line then ignore it */
        if( !*Buffer || *DmStrSpn( Buffer, " \t\r\n" ) == '\0' ) break;

        /* otherwise assume it was a line of unrecognized input, or a
         * recipe line out of place so print a message */

        Fatal( "Expecting macro or rule defn, found neither" );
        break;

      default:
        Fatal( "Internal -- UNKNOWN Parser state %d", State );
      }
    }
  }
}