aboutsummaryrefslogtreecommitdiff
path: root/tools/mhmake/src
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mhmake/src')
-rw-r--r--tools/mhmake/src/Makefile.am62
-rw-r--r--tools/mhmake/src/bison.cc1025
-rw-r--r--tools/mhmake/src/bison.h249
-rw-r--r--tools/mhmake/src/build.cpp1819
-rw-r--r--tools/mhmake/src/commandqueue.cpp410
-rw-r--r--tools/mhmake/src/commandqueue.h73
-rw-r--r--tools/mhmake/src/curdir.cpp37
-rw-r--r--tools/mhmake/src/curdir.h47
-rw-r--r--tools/mhmake/src/fileinfo.cpp363
-rw-r--r--tools/mhmake/src/fileinfo.h437
-rw-r--r--tools/mhmake/src/flex.skl3426
-rw-r--r--tools/mhmake/src/flexint.h63
-rw-r--r--tools/mhmake/src/flexlexer.h260
-rw-r--r--tools/mhmake/src/functions.cpp842
-rw-r--r--tools/mhmake/src/md5.cpp455
-rw-r--r--tools/mhmake/src/md5.h58
-rw-r--r--tools/mhmake/src/mhmake.cpp158
-rw-r--r--tools/mhmake/src/mhmakefileparser.cpp1451
-rw-r--r--tools/mhmake/src/mhmakefileparser.h383
-rw-r--r--tools/mhmake/src/mhmakelexer.l987
-rw-r--r--tools/mhmake/src/mhmakeparser.y255
-rw-r--r--tools/mhmake/src/refptr.h124
-rw-r--r--tools/mhmake/src/rule.cpp301
-rw-r--r--tools/mhmake/src/rule.h110
-rw-r--r--tools/mhmake/src/stdafx.cpp24
-rw-r--r--tools/mhmake/src/stdafx.h86
-rw-r--r--tools/mhmake/src/util.cpp780
-rw-r--r--tools/mhmake/src/util.h238
28 files changed, 14523 insertions, 0 deletions
diff --git a/tools/mhmake/src/Makefile.am b/tools/mhmake/src/Makefile.am
new file mode 100644
index 000000000..0478f1645
--- /dev/null
+++ b/tools/mhmake/src/Makefile.am
@@ -0,0 +1,62 @@
+SRCS = mhmakeparser.y mhmakelexer.l mhmake.cpp mhmakefileparser.cpp util.cpp \
+ functions.cpp fileinfo.cpp rule.cpp md5.c build.cpp curdir.cpp \
+ commandqueue.cpp
+
+if DEBUG
+bin_PROGRAMS=mhmake_dbg
+mhmake_dbg_SOURCES = $(SRCS)
+else
+bin_PROGRAMS=mhmake
+mhmake_SOURCES = $(SRCS)
+endif
+
+LEX=flex++
+AM_LFLAGS=-8
+
+YACC=bison++
+AM_YFLAGS=-d
+
+mhmakelexer.o: mhmakelexer.cpp mhmakelexer.h
+mhmakelexer.cpp: mhmakelexer.l
+mhmakelexer.h: mhmakelexer.l
+
+mhmakeparser.o: mhmakeparser.cpp mhmakeparser.h
+mhmakeparser.cpp: mhmakeparser.y
+mhmakeparser.h: mhmakeparser.y
+
+.l.cpp:
+ $(LEXCOMPILE) -S$(srcdir)/flexskel.cc -H$(srcdir)/flexskel.h -h$(@:%.cpp=%.h) -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $@
+ cat temp1234.456 >> $@
+ rm temp1234.456
+
+.y.cpp:
+ $(YACCCOMPILE) -S$(srcdir)/bison.cc -H$(srcdir)/bison.h -h$(@:%.cpp=%.h) -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $@
+ cat temp1234.456 >> $@
+ rm temp1234.456
+
+
+.l.h:
+ $(LEXCOMPILE) -S$(srcdir)/flexskel.cc -H$(srcdir)/flexskel.h -h$@ -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $(@:%.h=%.cpp)
+ cat temp1234.456 >> $(@:%.h=%.cpp)
+ rm temp1234.456
+
+.y.h:
+ $(YACCCOMPILE) -S$(srcdir)/bison.cc -H$(srcdir)/bison.h -h$@ -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $(@:%.h=%.cpp)
+ cat temp1234.456 >> $(@:%.h=%.cpp)
+ rm temp1234.456
+
+# set the include path found by configure
+INCLUDES= $(all_includes)
+
+LDADD = /usr/lib/libpopt.a
+
+# the library search path.
+if DEBUG
+mhmake_dbg_LDFLAGS = $(all_libraries)
+else
+mhmake_LDFLAGS = $(all_libraries)
+endif
diff --git a/tools/mhmake/src/bison.cc b/tools/mhmake/src/bison.cc
new file mode 100644
index 000000000..5a203f719
--- /dev/null
+++ b/tools/mhmake/src/bison.cc
@@ -0,0 +1,1025 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* HEADER SECTION */
+#if defined( _MSDOS ) || defined(MSDOS) || defined(__MSDOS__)
+#define __MSDOS_AND_ALIKE
+#endif
+#if defined(_WINDOWS) && defined(_MSC_VER)
+#define __HAVE_NO_ALLOCA
+#define __MSDOS_AND_ALIKE
+#endif
+
+#ifndef alloca
+#if defined( __GNUC__)
+#define alloca __builtin_alloca
+
+#elif (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+
+#elif defined (__MSDOS_AND_ALIKE)
+#include <malloc.h>
+#ifndef __TURBOC__
+/* MS C runtime lib */
+#define alloca _alloca
+#endif
+
+#elif defined(_AIX)
+#include <malloc.h>
+#pragma alloca
+
+#elif defined(__hpux)
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+
+#endif /* not _AIX not MSDOS, or __TURBOC__ or _AIX, not sparc. */
+#endif /* alloca not defined. */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+#ifdef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else
+#ifndef __STDC__
+#define const
+#endif
+#endif
+#include <stdio.h>
+#define YYBISON 1
+$/* %{ and %header{ and %union, during decl */
+#define YY_@_BISON 1
+#ifndef YY_@_COMPATIBILITY
+#ifndef YY_USE_CLASS
+#define YY_@_COMPATIBILITY 1
+#else
+#define YY_@_COMPATIBILITY 0
+#endif
+#endif
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YYLTYPE
+#ifndef YY_@_LTYPE
+#define YY_@_LTYPE YYLTYPE
+#endif
+#endif
+#ifdef YYSTYPE
+#ifndef YY_@_STYPE
+#define YY_@_STYPE YYSTYPE
+#endif
+#endif
+#ifdef YYDEBUG
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG YYDEBUG
+#endif
+#endif
+#ifdef YY_@_STYPE
+#ifndef yystype
+#define yystype YY_@_STYPE
+#endif
+#endif
+/* use goto to be compatible */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 1
+#endif
+#endif
+
+/* use no goto to be clean in C++ */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 0
+#endif
+
+#ifndef YY_@_PURE
+$/* YY_@_PURE */
+#endif
+
+/* section apres lecture def, avant lecture grammaire S2 */
+$/* prefix */
+#ifndef YY_@_DEBUG
+$/* YY_@_DEBUG */
+#endif
+
+
+#ifndef YY_@_LSP_NEEDED
+$ /* YY_@_LSP_NEEDED*/
+#endif
+
+
+
+/* DEFAULT LTYPE*/
+#ifdef YY_@_LSP_NEEDED
+#ifndef YY_@_LTYPE
+typedef struct yyltype
+{
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+}
+yyltype;
+
+#define YY_@_LTYPE yyltype
+#endif
+#endif
+/* DEFAULT STYPE*/
+ /* We used to use `unsigned long' as YY_@_STYPE on MSDOS,
+ but it seems better to be consistent.
+ Most programs should declare their own type anyway. */
+
+#ifndef YY_@_STYPE
+#define YY_@_STYPE int
+#endif
+/* DEFAULT MISCELANEOUS */
+#ifndef YY_@_PARSE
+#define YY_@_PARSE yyparse
+#endif
+#ifndef YY_@_LEX
+#define YY_@_LEX yylex
+#endif
+#ifndef YY_@_LVAL
+#define YY_@_LVAL yylval
+#endif
+#ifndef YY_@_LLOC
+#define YY_@_LLOC yylloc
+#endif
+#ifndef YY_@_CHAR
+#define YY_@_CHAR yychar
+#endif
+#ifndef YY_@_NERRS
+#define YY_@_NERRS yynerrs
+#endif
+#ifndef YY_@_DEBUG_FLAG
+#define YY_@_DEBUG_FLAG yydebug
+#endif
+#ifndef YY_@_ERROR
+#define YY_@_ERROR yyerror
+#endif
+#ifndef YY_@_PARSE_PARAM
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_@_PARSE_PARAM
+#ifndef YY_@_PARSE_PARAM_DEF
+#define YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+#endif
+#ifndef YY_@_PARSE_PARAM
+#define YY_@_PARSE_PARAM void
+#endif
+#endif
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YY_@_LTYPE
+#ifndef YYLTYPE
+#define YYLTYPE YY_@_LTYPE
+#else
+/* WARNING obsolete !!! user defined YYLTYPE not reported into generated header */
+#endif
+#endif
+#ifndef YYSTYPE
+#define YYSTYPE YY_@_STYPE
+#else
+/* WARNING obsolete !!! user defined YYSTYPE not reported into generated header */
+#endif
+#ifdef YY_@_PURE
+#ifndef YYPURE
+#define YYPURE YY_@_PURE
+#endif
+#endif
+#ifdef YY_@_DEBUG
+#ifndef YYDEBUG
+#define YYDEBUG YY_@_DEBUG
+#endif
+#endif
+#ifndef YY_@_ERROR_VERBOSE
+#ifdef YYERROR_VERBOSE
+#define YY_@_ERROR_VERBOSE YYERROR_VERBOSE
+#endif
+#endif
+#ifndef YY_@_LSP_NEEDED
+#ifdef YYLSP_NEEDED
+#define YY_@_LSP_NEEDED YYLSP_NEEDED
+#endif
+#endif
+#endif
+#ifndef YY_USE_CLASS
+/* TOKEN C */
+$ /* #defines tokens */
+#else
+/* CLASS */
+#ifndef YY_@_CLASS
+#define YY_@_CLASS @
+#endif
+#ifndef YY_@_INHERIT
+#define YY_@_INHERIT
+#endif
+#ifndef YY_@_MEMBERS
+#define YY_@_MEMBERS
+#endif
+#ifndef YY_@_LEX_BODY
+#define YY_@_LEX_BODY
+#endif
+#ifndef YY_@_ERROR_BODY
+#define YY_@_ERROR_BODY
+#endif
+#ifndef YY_@_CONSTRUCTOR_PARAM
+#define YY_@_CONSTRUCTOR_PARAM
+#endif
+#ifndef YY_@_CONSTRUCTOR_CODE
+#define YY_@_CONSTRUCTOR_CODE
+#endif
+#ifndef YY_@_CONSTRUCTOR_INIT
+#define YY_@_CONSTRUCTOR_INIT
+#endif
+/* choose between enum and const */
+#ifndef YY_@_USE_CONST_TOKEN
+#define YY_@_USE_CONST_TOKEN 0
+/* yes enum is more compatible with flex, */
+/* so by default we use it */
+#endif
+#if YY_@_USE_CONST_TOKEN != 0
+#ifndef YY_@_ENUM_TOKEN
+#define YY_@_ENUM_TOKEN yy_@_enum_token
+#endif
+#endif
+
+class YY_@_CLASS YY_@_INHERIT
+{
+public:
+#if YY_@_USE_CONST_TOKEN != 0
+/* static const int token ... */
+$ /* decl const */
+#else
+enum YY_@_ENUM_TOKEN { YY_@_NULL_TOKEN=0
+$ /* enum token */
+ }; /* end of enum declaration */
+#endif
+public:
+ int YY_@_PARSE (YY_@_PARSE_PARAM);
+#ifdef YY_@_PURE
+#else
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+ int YY_@_NERRS;
+ int YY_@_CHAR;
+#endif
+#if YY_@_DEBUG != 0
+ int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+#endif
+public:
+ YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM);
+public:
+ YY_@_MEMBERS
+};
+/* other declare folow */
+#if YY_@_USE_CONST_TOKEN != 0
+$ /* const YY_@_CLASS::token */
+#endif
+/*apres const */
+YY_@_CLASS::YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM) YY_@_CONSTRUCTOR_INIT
+{
+#if YY_@_DEBUG != 0
+YY_@_DEBUG_FLAG=0;
+#endif
+YY_@_CONSTRUCTOR_CODE;
+};
+#endif
+$ /* fattrs + tables */
+
+/* parser code folow */
+
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: dollar marks section change
+ the next is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#if YY_@_USE_GOTO != 0
+/*
+ SUPRESSION OF GOTO : on some C++ compiler (sun c++)
+ the goto is strictly forbidden if any constructor/destructor
+ is used in the whole function (very stupid isn't it ?)
+ so goto are to be replaced with a 'while/switch/case construct'
+ here are the macro to keep some apparent compatibility
+*/
+#define YYGOTO(lb) {yy_gotostate=lb;continue;}
+#define YYBEGINGOTO enum yy_labels yy_gotostate=yygotostart; \
+ for(;;) switch(yy_gotostate) { case yygotostart: {
+#define YYLABEL(lb) } case lb: {
+#define YYENDGOTO } }
+#define YYBEGINDECLARELABEL enum yy_labels {yygotostart
+#define YYDECLARELABEL(lb) ,lb
+#define YYENDDECLARELABEL };
+#else
+/* macro to keep goto */
+#define YYGOTO(lb) goto lb
+#define YYBEGINGOTO
+#define YYLABEL(lb) lb:
+#define YYENDGOTO
+#define YYBEGINDECLARELABEL
+#define YYDECLARELABEL(lb)
+#define YYENDDECLARELABEL
+#endif
+/* LABEL DECLARATION */
+YYBEGINDECLARELABEL
+ YYDECLARELABEL(yynewstate)
+ YYDECLARELABEL(yybackup)
+/* YYDECLARELABEL(yyresume) */
+ YYDECLARELABEL(yydefault)
+ YYDECLARELABEL(yyreduce)
+ YYDECLARELABEL(yyerrlab) /* here on detecting error */
+ YYDECLARELABEL(yyerrlab1) /* here on error raised explicitly by an action */
+ YYDECLARELABEL(yyerrdefault) /* current state does not do anything special for the error token. */
+ YYDECLARELABEL(yyerrpop) /* pop the current state because it cannot handle the error token */
+ YYDECLARELABEL(yyerrhandle)
+YYENDDECLARELABEL
+/* ALLOCA SIMULATION */
+/* __HAVE_NO_ALLOCA */
+#ifdef __HAVE_NO_ALLOCA
+int __alloca_free_ptr(char *ptr,char *ref)
+{if(ptr!=ref) free(ptr);
+ return 0;}
+
+#define __ALLOCA_alloca(size) malloc(size)
+#define __ALLOCA_free(ptr,ref) __alloca_free_ptr((char *)ptr,(char *)ref)
+
+#ifdef YY_@_LSP_NEEDED
+#define __ALLOCA_return(num) \
+ return( __ALLOCA_free(yyss,yyssa)+\
+ __ALLOCA_free(yyvs,yyvsa)+\
+ __ALLOCA_free(yyls,yylsa)+\
+ (num))
+#else
+#define __ALLOCA_return(num) \
+ return( __ALLOCA_free(yyss,yyssa)+\
+ __ALLOCA_free(yyvs,yyvsa)+\
+ (num))
+#endif
+#else
+#define __ALLOCA_return(num) return(num)
+#define __ALLOCA_alloca(size) alloca(size)
+#define __ALLOCA_free(ptr,ref)
+#endif
+
+/* ENDALLOCA SIMULATION */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (YY_@_CHAR = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT __ALLOCA_return(0)
+#define YYABORT __ALLOCA_return(1)
+#define YYERROR YYGOTO(yyerrlab1)
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL YYGOTO(yyerrlab)
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do\
+ if (YY_@_CHAR == YYEMPTY && yylen == 1)\
+ {\
+ YY_@_CHAR = (token), YY_@_LVAL = (value);\
+ yychar1 = YYTRANSLATE (YY_@_CHAR);\
+ YYPOPSTACK;\
+ YYGOTO(yybackup);\
+ }\
+ else\
+ {\
+ YY_@_ERROR ("syntax error: cannot back up"); YYERROR;\
+ }\
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YY_@_PURE
+/* UNPURE */
+#define YYLEX YY_@_LEX()
+#ifndef YY_USE_CLASS
+/* If nonreentrant, and not class , generate the variables here */
+int YY_@_CHAR; /* the lookahead symbol */
+ /* lookahead symbol */
+int YY_@_NERRS; /* number of parse errors so far */
+#ifdef YY_@_LSP_NEEDED
+YY_@_LTYPE YY_@_LLOC; /* location data for the lookahead */
+ /* symbol */
+#endif
+#endif
+
+
+#else
+/* PURE */
+#ifdef YY_@_LSP_NEEDED
+#define YYLEX YY_@_LEX(&YY_@_LVAL, &YY_@_LLOC)
+#else
+#define YYLEX YY_@_LEX(&YY_@_LVAL)
+#endif
+#endif
+#ifndef YY_USE_CLASS
+#if YY_@_DEBUG != 0
+int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+#endif
+
+
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+
+#ifdef __cplusplus
+static void __yy_bcopy (char *from, char *to, size_t count)
+#else
+#ifdef __STDC__
+static void __yy_bcopy (char *from, char *to, int count)
+#else
+static void __yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+#endif
+#endif
+{
+ register char *f = from;
+ register char *t = to;
+ register size_t i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+#endif
+
+int
+#ifdef YY_USE_CLASS
+ YY_@_CLASS::
+#endif
+ YY_@_PARSE(YY_@_PARSE_PARAM)
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+/* parameter definition without protypes */
+YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YY_@_STYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1=0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YY_@_STYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YY_@_STYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YY_@_LTYPE *yyls = yylsa;
+ YY_@_LTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YY_@_PURE
+ int YY_@_CHAR;
+ YY_@_STYPE YY_@_LVAL;
+ int YY_@_NERRS;
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+#endif
+
+ YY_@_STYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+/* start loop, in which YYGOTO may be used. */
+YYBEGINGOTO
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Starting parse\n");
+#endif
+ yystate = 0;
+ yyerrstatus = 0;
+ YY_@_NERRS = 0;
+ YY_@_CHAR = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YY_@_LSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+YYLABEL(yynewstate)
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YY_@_STYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ size_t size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YY_@_LSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YY_@_LSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ YY_@_ERROR("parser stack overflow");
+ __ALLOCA_return(2);
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) __ALLOCA_alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ __ALLOCA_free(yyss1,yyssa);
+ yyvs = (YY_@_STYPE *) __ALLOCA_alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+ __ALLOCA_free(yyvs1,yyvsa);
+#ifdef YY_@_LSP_NEEDED
+ yyls = (YY_@_LTYPE *) __ALLOCA_alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+ __ALLOCA_free(yyls1,yylsa);
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YY_@_LSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ YYGOTO(yybackup);
+YYLABEL(yybackup)
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* YYLABEL(yyresume) */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ YYGOTO(yydefault);
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (YY_@_CHAR == YYEMPTY)
+ {
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ YY_@_CHAR = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (YY_@_CHAR <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ YY_@_CHAR = YYEOF; /* Don't call YYLEX any more */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(YY_@_CHAR);
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ fprintf (stderr, "Next token is %d (%s", YY_@_CHAR, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, YY_@_CHAR, YY_@_LVAL);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ YYGOTO(yydefault);
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrlab);
+ yyn = -yyn;
+ YYGOTO(yyreduce);
+ }
+ else if (yyn == 0)
+ YYGOTO(yyerrlab);
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Shifting token %d (%s), ", YY_@_CHAR, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (YY_@_CHAR != YYEOF)
+ YY_@_CHAR = YYEMPTY;
+
+ *++yyvsp = YY_@_LVAL;
+#ifdef YY_@_LSP_NEEDED
+ *++yylsp = YY_@_LLOC;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ YYGOTO(yynewstate);
+
+/* Do the default action for the current state. */
+YYLABEL(yydefault)
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ YYGOTO(yyerrlab);
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+YYLABEL(yyreduce)
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+$ /* the action file gets copied in in place of this dollarsign */
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YY_@_LSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YY_@_LSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ *yylsp= YY_@_LLOC;
+ //yylsp->first_line = YY_@_LLOC.first_line;
+ //yylsp->first_column = YY_@_LLOC.first_column;
+ //yylsp->last_line = (yylsp-1)->last_line;
+ //yylsp->last_column = (yylsp-1)->last_column;
+ //yylsp->text = 0;
+ }
+ else
+ {
+ *yylsp = *(yylsp+yylen-1);
+ //yylsp->last_line = (yylsp+yylen-1)->last_line;
+ //yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ YYGOTO(yynewstate);
+
+YYLABEL(yyerrlab) /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++YY_@_NERRS;
+
+#ifdef YY_@_ERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ size_t size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ YY_@_ERROR(msg);
+ free(msg);
+ }
+ else
+ YY_@_ERROR ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YY_@_ERROR_VERBOSE */
+ YY_@_ERROR("parse error");
+ }
+
+ YYGOTO(yyerrlab1);
+YYLABEL(yyerrlab1) /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (YY_@_CHAR == YYEOF)
+ YYABORT;
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Discarding token %d (%s).\n", YY_@_CHAR, yytname[yychar1]);
+#endif
+
+ YY_@_CHAR = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ YYGOTO(yyerrhandle);
+
+YYLABEL(yyerrdefault) /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) YYGOTO(yydefault);
+#endif
+
+YYLABEL(yyerrpop) /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YY_@_LSP_NEEDED
+ yylsp--;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+YYLABEL(yyerrhandle)
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrdefault);
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ YYGOTO(yyerrdefault);
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrpop);
+ yyn = -yyn;
+ YYGOTO(yyreduce);
+ }
+ else if (yyn == 0)
+ YYGOTO(yyerrpop);
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = YY_@_LVAL;
+#ifdef YY_@_LSP_NEEDED
+ *++yylsp = YY_@_LLOC;
+#endif
+
+ yystate = yyn;
+ YYGOTO(yynewstate);
+/* end loop, in which YYGOTO may be used. */
+ YYENDGOTO
+}
+
+/* END */
+$ /* section 3 */
+
+/* AFTER END , NEVER READ !!! */
+
diff --git a/tools/mhmake/src/bison.h b/tools/mhmake/src/bison.h
new file mode 100644
index 000000000..0724ae112
--- /dev/null
+++ b/tools/mhmake/src/bison.h
@@ -0,0 +1,249 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+/* before anything */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+#ifdef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else
+#endif
+#include <stdio.h>
+$ /* %{ and %header{ and %union, during decl */
+#ifndef YY_@_COMPATIBILITY
+#ifndef YY_USE_CLASS
+#define YY_@_COMPATIBILITY 1
+#else
+#define YY_@_COMPATIBILITY 0
+#endif
+#endif
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YYLTYPE
+#ifndef YY_@_LTYPE
+#define YY_@_LTYPE YYLTYPE
+/* WARNING obsolete !!! user defined YYLTYPE not reported into generated header */
+/* use %define LTYPE */
+#endif
+#endif
+#ifdef YYSTYPE
+#ifndef YY_@_STYPE
+#define YY_@_STYPE YYSTYPE
+/* WARNING obsolete !!! user defined YYSTYPE not reported into generated header */
+/* use %define STYPE */
+#endif
+#endif
+#ifdef YYDEBUG
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG YYDEBUG
+/* WARNING obsolete !!! user defined YYDEBUG not reported into generated header */
+/* use %define DEBUG */
+#endif
+#endif
+#ifdef YY_@_STYPE
+#ifndef yystype
+#define yystype YY_@_STYPE
+#endif
+#endif
+/* use goto to be compatible */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 1
+#endif
+#endif
+
+/* use no goto to be clean in C++ */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 0
+#endif
+
+#ifndef YY_@_PURE
+$/* YY_@_PURE */
+#endif
+$/* prefix */
+#ifndef YY_@_DEBUG
+$/* YY_@_DEBUG */
+#endif
+#ifndef YY_@_LSP_NEEDED
+$ /* YY_@_LSP_NEEDED*/
+#endif
+/* DEFAULT LTYPE*/
+#ifdef YY_@_LSP_NEEDED
+#ifndef YY_@_LTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YY_@_LTYPE yyltype
+#endif
+#endif
+/* DEFAULT STYPE*/
+#ifndef YY_@_STYPE
+#define YY_@_STYPE int
+#endif
+/* DEFAULT MISCELANEOUS */
+#ifndef YY_@_PARSE
+#define YY_@_PARSE yyparse
+#endif
+#ifndef YY_@_LEX
+#define YY_@_LEX yylex
+#endif
+#ifndef YY_@_LVAL
+#define YY_@_LVAL yylval
+#endif
+#ifndef YY_@_LLOC
+#define YY_@_LLOC yylloc
+#endif
+#ifndef YY_@_CHAR
+#define YY_@_CHAR yychar
+#endif
+#ifndef YY_@_NERRS
+#define YY_@_NERRS yynerrs
+#endif
+#ifndef YY_@_DEBUG_FLAG
+#define YY_@_DEBUG_FLAG yydebug
+#endif
+#ifndef YY_@_ERROR
+#define YY_@_ERROR yyerror
+#endif
+
+#ifndef YY_@_PARSE_PARAM
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_@_PARSE_PARAM
+#ifndef YY_@_PARSE_PARAM_DEF
+#define YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+#endif
+#ifndef YY_@_PARSE_PARAM
+#define YY_@_PARSE_PARAM void
+#endif
+#endif
+
+/* TOKEN C */
+#ifndef YY_USE_CLASS
+
+#ifndef YY_@_PURE
+extern YY_@_STYPE YY_@_LVAL;
+#endif
+
+$ /* #defines token */
+/* after #define tokens, before const tokens S5*/
+#else
+#ifndef YY_@_CLASS
+#define YY_@_CLASS @
+#endif
+
+#ifndef YY_@_INHERIT
+#define YY_@_INHERIT
+#endif
+#ifndef YY_@_MEMBERS
+#define YY_@_MEMBERS
+#endif
+#ifndef YY_@_LEX_BODY
+#define YY_@_LEX_BODY
+#endif
+#ifndef YY_@_ERROR_BODY
+#define YY_@_ERROR_BODY
+#endif
+#ifndef YY_@_CONSTRUCTOR_PARAM
+#define YY_@_CONSTRUCTOR_PARAM
+#endif
+/* choose between enum and const */
+#ifndef YY_@_USE_CONST_TOKEN
+#define YY_@_USE_CONST_TOKEN 0
+/* yes enum is more compatible with flex, */
+/* so by default we use it */
+#endif
+#if YY_@_USE_CONST_TOKEN != 0
+#ifndef YY_@_ENUM_TOKEN
+#define YY_@_ENUM_TOKEN yy_@_enum_token
+#endif
+#endif
+
+class YY_@_CLASS YY_@_INHERIT
+{
+public:
+#if YY_@_USE_CONST_TOKEN != 0
+/* static const int token ... */
+$ /* decl const */
+#else
+enum YY_@_ENUM_TOKEN { YY_@_NULL_TOKEN=0
+$ /* enum token */
+ }; /* end of enum declaration */
+#endif
+public:
+ int YY_@_PARSE(YY_@_PARSE_PARAM);
+#ifdef YY_@_PURE
+#else
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+ int YY_@_NERRS;
+ int YY_@_CHAR;
+#endif
+#if YY_@_DEBUG != 0
+public:
+ int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+#endif
+public:
+ YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM);
+public:
+ YY_@_MEMBERS
+};
+/* other declare folow */
+#endif
+
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifndef YYSTYPE
+#define YYSTYPE YY_@_STYPE
+#endif
+
+#ifndef YYLTYPE
+#define YYLTYPE YY_@_LTYPE
+#endif
+#ifndef YYDEBUG
+#ifdef YY_@_DEBUG
+#define YYDEBUG YY_@_DEBUG
+#endif
+#endif
+
+#endif
+/* END */
+$ /* section 3 %header{ */
+ /* AFTER END , NEVER READ !!! */
+
+
+
diff --git a/tools/mhmake/src/build.cpp b/tools/mhmake/src/build.cpp
new file mode 100644
index 000000000..9efa90932
--- /dev/null
+++ b/tools/mhmake/src/build.cpp
@@ -0,0 +1,1819 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "mhmakefileparser.h"
+#include "mhmakeparser.h"
+#include "rule.h"
+#include "util.h"
+
+/* Calling py2exe is only implemented on windows for now. */
+#ifdef WIN32
+
+/* Python exe create script in parts:
+import zipfile,tempfile,shutil,os
+
+def UpdateZipFile(SrcZip,DestZip):
+ NewZip=tempfile.mktemp('.zip')
+
+ Src=zipfile.ZipFile(SrcZip)
+
+ New=zipfile.ZipFile(NewZip,'w')
+
+ InFile={}
+ for name in Src.namelist():
+ InFile[name]=1
+ New.writestr(name,Src.read(name))
+ Src.close()
+
+ try:
+ Dest=zipfile.ZipFile(DestZip)
+
+ for name in Dest.namelist():
+ if not InFile.has_key(name):
+ New.writestr(name,Dest.read(name))
+ Dest.close()
+ except IOError:
+ pass
+ New.close()
+
+ shutil.move(NewZip,DestZip)
+
+OutFileName=tempfile.mktemp('.py')
+pOut=open(OutFileName,'w')
+
+Script=r'<PYTHONSCRIPT>'
+DirScript=os.path.split(Script)[0]
+pOut.write(r'''
+
+from distutils.core import setup
+import py2exe
+import sys
+sys.path.append(r'%s')
+setup(zipfile=None, console=[r'%s'])
+'''%(DirScript,Script))
+
+pOut.close()
+
+import os
+
+stdin,stdout=os.popen4(r'"<PYTHONEXE>" %s py2exe'%OutFileName);
+stdout.read()
+stdout.close()
+stdin.close()
+
+OutDir=os.path.split(Script)[0]
+
+import shutil
+def CopyFiles(Src,Dest):
+ for File in os.listdir(Src):
+ SrcDir=os.path.join(Src,File)
+ if File=='library.zip':
+ UpdateZipFile(os.path.join(Src,File),os.path.join(Dest,File))
+ elif os.path.isdir(SrcDir):
+ DestDir=os.path.join(Dest,File)
+ os.mkdir(DestDir)
+ CopyFiles(SrcDir,DestDir)
+ else:
+ shutil.copy(os.path.join(Src,File),os.path.join(Dest,File))
+
+if os.path.isdir('dist'):
+ CopyFiles('dist',OutDir)
+
+ try:
+ shutil.rmtree('dist')
+ except:
+ pass
+ try:
+ shutil.rmtree('build')
+ except:
+ pass
+os.remove(OutFileName)
+*/
+
+static const string PythonScriptPart1=
+"import zipfile,tempfile,shutil,os\n"
+"\n"
+"def UpdateZipFile(SrcZip,DestZip):\n"
+" NewZip=tempfile.mktemp('.zip')\n"
+"\n"
+" Src=zipfile.ZipFile(SrcZip)\n"
+"\n"
+" New=zipfile.ZipFile(NewZip,'w')\n"
+"\n"
+" InFile={}\n"
+" for name in Src.namelist():\n"
+" InFile[name]=1\n"
+" New.writestr(name,Src.read(name))\n"
+" Src.close()\n"
+"\n"
+" try:\n"
+" Dest=zipfile.ZipFile(DestZip)\n"
+"\n"
+" for name in Dest.namelist():\n"
+" if not InFile.has_key(name):\n"
+" New.writestr(name,Dest.read(name))\n"
+" Dest.close()\n"
+" except IOError:\n"
+" pass\n"
+" New.close()\n"
+"\n"
+" shutil.move(NewZip,DestZip)\n"
+"\n"
+"OutFileName=tempfile.mktemp('.py')\n"
+"pOut=open(OutFileName,'w')\n"
+"\n"
+"Script=r'"
+;
+
+static const string PythonScriptPart2=
+"'\n"
+"DirScript=os.path.split(Script)[0]\n"
+"\n"
+"pOut.write(r'''\n"
+"\n"
+"from distutils.core import setup\n"
+"import py2exe\n"
+"import sys\n"
+"sys.path.append(r'%s')\n"
+"setup(console=[r'%s'])\n"
+"'''%(DirScript,Script))\n"
+"\n"
+"pOut.close()\n"
+"\n"
+"import os\n"
+"\n"
+"stdin,stdout=os.popen4(r'"
+;
+
+static const string PythonScriptPart3=
+"%s py2exe'%OutFileName);\n"
+"stdout.read()\n"
+"stdout.close()\n"
+"stdin.close()\n"
+"\n"
+"OutDir=os.path.split(Script)[0]\n"
+"\n"
+"import shutil\n"
+"def CopyFiles(Src,Dest):\n"
+" for File in os.listdir(Src):\n"
+" SrcDir=os.path.join(Src,File)\n"
+" if File=='library.zip':\n"
+" UpdateZipFile(os.path.join(Src,File),os.path.join(Dest,File))\n"
+" elif os.path.isdir(SrcDir):\n"
+" DestDir=os.path.join(Dest,File)\n"
+" os.mkdir(DestDir)\n"
+" CopyFiles(SrcDir,DestDir)\n"
+" else:\n"
+" shutil.copy(os.path.join(Src,File),os.path.join(Dest,File))\n"
+"\n"
+"if os.path.isdir('dist'):\n"
+" CopyFiles('dist',OutDir)\n"
+"\n"
+" try:\n"
+" shutil.rmtree('dist')\n"
+" except:\n"
+" pass\n"
+" try:\n"
+" shutil.rmtree('build')\n"
+" except:\n"
+" pass\n"
+"os.remove(OutFileName)\n"
+;
+/*****************************************************************************/
+/* Converts a python script to an executable if py2exe is installed */
+
+void mhmakefileparser::CreatePythonExe(const string &FullCommand)
+{
+ /* First create a python script to run */
+ cout << "Converting "<<FullCommand<<endl;
+
+ string PythonScript;
+ PythonScript+=PythonScriptPart1;
+ PythonScript+=FullCommand;
+ PythonScript+=PythonScriptPart2;
+ PythonScript+=GetPythonExe();
+ PythonScript+=PythonScriptPart3;
+
+ char Filename[MAX_PATH];
+ int Nr=0;
+ FILE *pFile=(FILE*)1;
+ while (1)
+ {
+ sprintf(Filename,"%s\\tmp%d.py",m_MakeDir->GetFullFileName().c_str(),Nr);
+ pFile=fopen(Filename,"r");
+ if (!pFile)
+ break;
+ fclose(pFile);
+ Nr++;
+ }
+ pFile=fopen(Filename,"w");
+ fprintf(pFile,"%s",PythonScript.c_str());
+ fclose(pFile);
+
+ string GenExeCommand=GetPythonExe();
+ GenExeCommand+=QuoteFileName(Filename);
+
+ string Output;
+ ExecuteCommand(GenExeCommand,&Output);
+
+ remove(Filename);
+}
+#endif
+
+/*****************************************************************************/
+int mhmakefileparser::SearchPath(const char *szCommand, const char *pExt, size_t Len, char *szFullCommand,char **pFilePart) const
+{
+ static vector< fileinfo* > vSearchPath;
+
+ string Command(szCommand);
+ if (pExt)
+ Command+=pExt;
+ vector< fileinfo* >::iterator It;
+ vector< fileinfo* >::iterator ItEnd;
+
+ fileinfo* pCommandFile=GetFileInfo(Command,m_MakeDir);
+ if (pCommandFile->Exists())
+ {
+ goto found;
+ }
+ pCommandFile->InvalidateDate(); // It could be created in the makefile later
+ if (!vSearchPath.size())
+ {
+ char *pPath=getenv(PATH);
+ if (!pPath)
+ return 0;
+ char *Path=strdup(pPath); // To be able to use strtok
+ char *pTok=strtok(Path,OSPATHENVSEPSTR);
+ while (pTok)
+ {
+ vSearchPath.push_back(GetFileInfo(pTok,m_MakeDir));
+ pTok=strtok(NULL,OSPATHENVSEPSTR);
+ }
+ free(Path);
+ }
+ It=vSearchPath.begin();
+ ItEnd=vSearchPath.end();
+ while (It!=ItEnd)
+ {
+ pCommandFile=GetFileInfo(Command,*It);
+ if (pCommandFile->Exists())
+ goto found;
+ It++;
+ }
+
+ return 0;
+
+found:
+ string FullCommand=pCommandFile->GetFullFileName();
+ size_t CommandLen=FullCommand.size();
+ if (CommandLen>Len-1)
+ {
+ throw string("Command to long: ") + FullCommand;
+ }
+ strcpy(szFullCommand,FullCommand.c_str());
+ return 1;
+}
+
+/*****************************************************************************/
+string mhmakefileparser::SearchCommand(const string &Command, const string &Extension) const
+{
+ char FullCommand[MAX_PATH]="";
+ unsigned long Size=sizeof(FullCommand);
+ const char *pExt;
+ if (Extension.empty())
+ pExt=NULL;
+ else
+ pExt=Extension.c_str();
+ if (SearchPath(UnquoteFileName(Command).c_str(),pExt,MAX_PATH,FullCommand,NULL))
+ return FullCommand;
+#ifdef WIN32
+ /* See if we have a path for python.exe in the registry */
+ HKEY hKey;
+ string RegEntry=Command;
+ if (pExt)
+ {
+ RegEntry+=Extension;
+ }
+ string KeyName=string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\")+RegEntry;
+ if (ERROR_SUCCESS!=RegOpenKey(HKEY_LOCAL_MACHINE,KeyName.c_str(),&hKey))
+ return FullCommand;
+
+ RegQueryValueEx(hKey,NULL,NULL,NULL,(LPBYTE)FullCommand,&Size);
+
+ RegCloseKey(hKey);
+#endif
+
+ return FullCommand;
+}
+
+/*****************************************************************************/
+/* Deletes a complete directory or files with wildcard. It can be assumed that the Directory passed does exist */
+static bool DeleteDir(const string &Dir,const string WildSearch="*",bool bRemoveDir=true)
+{
+ bool Error=false;
+ string Pattern=Dir+OSPATHSEP+WildSearch;
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ mh_pid_t hFind=FindFirstFile(Pattern.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return Error;
+ }
+
+ do
+ {
+ /* Only handle items which are not . and .. */
+ if (FindData.cFileName[0]!='.' || (FindData.cFileName[1] && (FindData.cFileName[1]!='.' || FindData.cFileName[2])) )
+ {
+ string FileName=Dir+OSPATHSEP+FindData.cFileName;
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ {
+ Error = DeleteDir(FileName);
+ }
+ else
+ {
+ Error = (-1==remove(FileName.c_str()));
+ }
+ }
+ } while (FindNextFile(hFind,&FindData));
+
+ FindClose(hFind);
+
+ if (bRemoveDir)
+ Error = (0==RemoveDirectory(Dir.c_str()));
+#else
+ glob_t Res;
+ if (glob (Pattern.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return Error;
+
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ int Len=strlen(Res.gl_pathv[i])-1;
+ if (Res.gl_pathv[i][Len]=='/')
+ {
+ Res.gl_pathv[i][Len]=0;
+ Error = DeleteDir(Res.gl_pathv[i]);
+ }
+ else
+ {
+ Error = (-1==remove(Res.gl_pathv[i]));
+ }
+ }
+
+ globfree(&Res);
+
+ if (bRemoveDir)
+ Error = (-1==remove(Dir.c_str()));
+#endif
+
+ return Error;
+}
+
+/*****************************************************************************/
+mh_pid_t mhmakefileparser::DeleteFiles(const string &Params) const
+{
+ bool IgnoreError=false;
+ vector<fileinfo*> Files;
+
+ // First check if the first parameter is -e meaning don't give an error when file does not exist
+ if (Params[1]=='-')
+ {
+ if (Params[2]=='e')
+ {
+ IgnoreError=true;
+ SplitToItems(Params.substr(4),Files);
+ }
+ else
+ {
+ cerr << "Invalid option "<<Params[1]<<" in del statement\n";
+ return (mh_pid_t)-1;
+ }
+ }
+ else
+ {
+ SplitToItems(Params,Files);
+ }
+
+ vector<fileinfo*>::const_iterator It=Files.begin();
+ while (It!=Files.end())
+ {
+ fileinfo* pFile=*It++;
+ string DirSearch="*";
+ bool bRemoveDir=true;
+
+ /* Now check if there is a wildcard */
+ if (pFile->GetFullFileName().find('*')!=string::npos)
+ {
+ DirSearch=pFile->GetName();
+ pFile=pFile->GetDir();
+ bRemoveDir=false;
+ }
+
+ pFile->InvalidateDate();
+ if (IgnoreError && !pFile->Exists() && !pFile->IsDir())
+ {
+ continue;
+ }
+
+ const string &FileName=pFile->GetFullFileName();
+ if (pFile->IsDir())
+ {
+ if (DeleteDir(FileName,DirSearch,bRemoveDir) && !IgnoreError)
+ {
+ cerr << "Error deleting "<<FileName<<endl;
+ return (mh_pid_t)-1;
+ }
+ }
+ else
+ {
+ if (-1==remove(FileName.c_str()) && !IgnoreError)
+ {
+ cerr << "Error deleting "<<FileName<<endl;
+ return (mh_pid_t)-1;
+ }
+ }
+ }
+ return (mh_pid_t)0;
+}
+
+/*****************************************************************************/
+/* pDest can be a directory or a file */
+static bool CopyFile(fileinfo* pSrc, fileinfo* pDest)
+{
+ if (pDest->IsDir())
+ {
+ pDest=GetFileInfo(pSrc->GetName(),pDest);
+ }
+ string SrcFileName=pSrc->GetFullFileName();
+ string DestFileName=pDest->GetFullFileName();
+
+ /* Now copy the file */
+ FILE *pSrcFile=fopen(SrcFileName.c_str(),"rb");
+ if (!pSrcFile)
+ {
+ cerr << "copy: error opening file "<<SrcFileName<<endl;
+ return false;
+ }
+ FILE *pDestFile=fopen(DestFileName.c_str(),"wb");
+ if (!pDestFile)
+ {
+ cerr << "copy: error creating file "<<DestFileName<<endl;
+ return false;
+ }
+ char Buf[4096];
+ size_t Ret;
+
+ while ( (Ret=fread(Buf,1,sizeof(Buf),pSrcFile)) > 0)
+ {
+ fwrite(Buf,1,Ret,pDestFile);
+ }
+ fclose(pSrcFile);
+ fclose(pDestFile);
+ pDest->InvalidateDate();
+ return true;
+}
+
+/*****************************************************************************/
+/* Copies a complete directory to a destination (currenlty not recursive */
+static bool CopyDir(fileinfo* pDir, fileinfo* pDest,const string WildSearch="*")
+{
+ bool Error=true;
+ string Pattern=pDir->GetFullFileName()+OSPATHSEP+WildSearch;
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ mh_pid_t hFind=FindFirstFile(Pattern.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+
+ do
+ {
+ /* Only handle items which are not . and .. */
+ if (FindData.cFileName[0]!='.' || (FindData.cFileName[1] && (FindData.cFileName[1]!='.' || FindData.cFileName[2])) )
+ {
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
+ continue;
+ fileinfo* pSrc=GetFileInfo(FindData.cFileName,pDir);
+
+ if (pSrc->IsDir())
+ {
+ fileinfo* pNewDest=GetFileInfo(FindData.cFileName,pDest);
+ if (!pNewDest->IsDir())
+ {
+ if (pNewDest->Exists())
+ {
+ cerr << pNewDest->GetFullFileName() << " exists and is not a directory.\n";
+ Error = false;
+ goto exit;
+ }
+ if (!CreateDirectory(pNewDest->GetFullFileName().c_str(),NULL))
+ {
+ cerr << "Error creating directory " << pNewDest->GetFullFileName() << endl;
+ Error = false;
+ goto exit;
+ }
+ pNewDest->InvalidateDate();
+ }
+ Error = CopyDir(pSrc,pNewDest);
+ if (!Error) goto exit;
+ }
+ else
+ {
+ Error = CopyFile(pSrc,pDest);
+ if (!Error) goto exit;
+ }
+ }
+ } while (FindNextFile(hFind,&FindData));
+
+exit:
+ FindClose(hFind);
+#else
+ glob_t Res;
+ if (glob (Pattern.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return Error;
+
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ fileinfo* pSrc=GetFileInfo(Res.gl_pathv[i],pDir);
+ if (pSrc->IsDir())
+ {
+ *(strrchr(Res.gl_pathv[i],'/'))='\0';
+ const char *SrcDirName=strrchr(Res.gl_pathv[i],'/')+1;
+
+ if (SrcDirName[0]=='.')
+ continue;
+
+ fileinfo* pNewDest=GetFileInfo(SrcDirName,pDest);
+ if (!pNewDest->IsDir())
+ {
+ if (pNewDest->Exists())
+ {
+ cerr << pNewDest->GetQuotedFullFileName() << " exists and is not a directory.\n";
+ Error = false;
+ goto exit;
+ }
+ if (-1==mkdir(pNewDest->GetFullFileName().c_str(),0777))
+ {
+ cerr << "Error creating directory " << pNewDest->GetQuotedFullFileName() << endl;
+ Error = false;
+ goto exit;
+ }
+ pNewDest->InvalidateDate();
+ }
+ Error = CopyDir(pSrc,pNewDest);
+ if (!Error) goto exit;
+ }
+ else
+ {
+ Error = CopyFile(GetFileInfo(Res.gl_pathv[i],pDir),pDest);
+ if (!Error) goto exit;
+ }
+ }
+
+exit:
+ globfree(&Res);
+#endif
+
+ return Error;
+}
+
+/*****************************************************************************/
+mh_pid_t mhmakefileparser::EchoCommand(const string &Params) const
+{
+ // Find the first > character
+ size_t Pos=Params.find_first_of('>');
+ if (Pos==string::npos)
+ {
+ // Just echo it
+ cout << Params << endl;
+ }
+ else
+ {
+ FILE *pfFile;
+ /* Extra the filename */
+ string Filename;
+ if (Params[Pos+1]=='>')
+ {
+ NextItem(Params.substr(Pos+2).c_str(),Filename);
+ fileinfo* pFile=GetFileInfo(Filename,m_MakeDir);
+ // Open file in append
+ pfFile=fopen(pFile->GetFullFileName().c_str(),"a");
+ }
+ else
+ {
+ NextItem(Params.substr(Pos+1).c_str(),Filename);
+ fileinfo* pFile=GetFileInfo(Filename,m_MakeDir);
+ pfFile=fopen(pFile->GetFullFileName().c_str(),"w");
+ }
+ if (!pfFile)
+ {
+ cerr << "Error opening file "<<Filename<<endl;
+ return (mh_pid_t)-1;
+ }
+ int Begin=0;
+ while (Params[Begin]==' ' || Params[Begin] == '\t') Begin++; // Strip leading white space
+ string EchoStr=Params.substr(Begin,Pos-1)+"\n";
+ if (EchoStr.length()!=fwrite(EchoStr.c_str(),1,EchoStr.length(),pfFile))
+ {
+ cerr << "Error writing file "<<Filename<<endl;
+ return (mh_pid_t)-1;
+ }
+ fclose(pfFile);
+ }
+ return (mh_pid_t)0;
+}
+
+/*****************************************************************************/
+mh_pid_t mhmakefileparser::CopyFiles(const string &Params) const
+{
+ vector<fileinfo*> Files;
+
+ SplitToItems(Params,Files);
+
+ size_t NrSrcs=Files.size()-1;
+
+ if (NrSrcs<1)
+ {
+ cerr << "Wrong number of arguments in copy: "<<Params<<endl;
+ return false;
+ }
+
+ fileinfo* pDest=Files[NrSrcs];
+ if (NrSrcs>1 && !pDest->IsDir())
+ {
+ cerr << "copy: Destination must be a directory when more then one source : "<<Params<<endl;
+ return (mh_pid_t)-1;
+ }
+
+ for (size_t i=0; i<NrSrcs; i++)
+ {
+ fileinfo* pSrc=Files[i];
+
+ string SrcFileName=pSrc->GetFullFileName();
+
+ if (pSrc->IsDir())
+ {
+ SrcFileName+=OSPATHSEPSTR"*";
+ pSrc=GetFileInfo(SrcFileName,m_MakeDir);
+ }
+
+ //cerr << "copy "<<pSrc->GetFullFileName()<<" "<<pDest->GetFullFileName()<<endl;
+ /* Now check if there is a wildcard */
+ if (SrcFileName.find('*')!=string::npos)
+ {
+ if (!CopyDir(pSrc->GetDir(), pDest, pSrc->GetName()))
+ {
+ cerr << "copy: Error copying directory: " << Params << endl;
+ return (mh_pid_t)-1;
+ }
+ }
+ else
+ {
+ if (!CopyFile(pSrc,pDest))
+ {
+ cerr << "copy: Error copying file: " << Params << endl;
+ return (mh_pid_t)-1;
+ }
+ }
+ }
+
+ return (mh_pid_t)0;
+
+}
+
+/*****************************************************************************/
+mh_pid_t mhmakefileparser::TouchFiles(const string &Params) const
+{
+ vector<fileinfo*> Files;
+
+ SplitToItems(Params,Files);
+
+ vector<fileinfo*>::const_iterator It=Files.begin();
+ while (It!=Files.end())
+ {
+ fileinfo* pFile=*It++;
+ const string &FileName=pFile->GetFullFileName();
+
+ /* Since this can be part of a list of commands for a certain rule, and it is possible that the file
+ * was generated by one on the previous commands, we first need the invalidate the date so that the
+ * existance checking is done again */
+ pFile->InvalidateDate();
+
+ if (pFile->IsDir())
+ {
+ cerr << "touch: Cannot touch a directory: " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ if (pFile->Exists())
+ {
+ int fd;
+ char c;
+ int status = 0;
+ struct stat st;
+ int Ret;
+ int saved_errno = 0;
+
+ fd = open (FileName.c_str(), O_RDWR);
+ if (fd<0)
+ {
+ st.st_size=0;
+ }
+ else
+ {
+ if (fstat (fd, &st) < 0)
+ {
+ cerr << "touch: Cannot stat file " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ }
+
+ if (st.st_size == 0)
+ {
+ FILE *pFile;
+ if (fd>=0 && close(fd) < 0)
+ {
+ cerr << "touch: Error closing file " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ /*Re-Create an empty file */
+ pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ cerr << "touch: Cannot create file: " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ fclose(pFile);
+ }
+ else
+ {
+ Ret=read (fd, &c, sizeof(c));
+ if (Ret!=sizeof(c) && Ret!=EOF)
+ {
+ cerr << "touch: Cannot read file " << FileName << ": "<<Ret<<endl;
+ return (mh_pid_t)-1;
+ }
+ if (lseek (fd, (off_t) 0, SEEK_SET) < 0)
+ {
+ cerr << "touch: Error changing file pointer " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ if (write (fd, &c, sizeof c) != sizeof(c))
+ {
+ cerr << "touch: Error writing file " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ if (close (fd) < 0)
+ {
+ cerr << "touch: Error closing file " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ }
+ }
+ else
+ {
+ /* Create an empty file */
+ FILE *pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ cerr << "touch: Cannot create file: " << FileName << endl;
+ return (mh_pid_t)-1;
+ }
+ fclose(pFile);
+ }
+ pFile->InvalidateDate();
+ }
+ return (mh_pid_t)0;
+}
+
+/*****************************************************************************/
+const string &mhmakefileparser::GetPythonExe() const
+{
+ static string PythonExe;
+ if (PythonExe.empty())
+ {
+ string FullCommand=SearchCommand(PYTHONEXE);
+ if (!FullCommand.empty())
+ {
+ PythonExe=QuoteFileName(FullCommand)+" ";
+ }
+ else
+ {
+ cerr<<"python executable not found in path and registry.\n";
+ exit(1);
+ }
+ }
+ return PythonExe;
+}
+
+/*****************************************************************************/
+static const string &GetComspec()
+{
+ static string Comspec;
+ if (Comspec.empty())
+ {
+ const char *pComspec=getenv(COMSPEC);
+ if (pComspec)
+ {
+ Comspec=getenv(COMSPEC);
+ #ifdef WIN32
+ Comspec+=" /c ";
+ #else
+ Comspec+=" -c \"";
+ #endif
+ }
+ else
+ {
+ #ifdef WIN32
+ Comspec="cmd.exe /c ";
+ #else
+ Comspec="sh -c \"";
+ #endif
+ }
+ }
+ return Comspec;
+}
+
+/*****************************************************************************/
+string mhmakefileparser::GetFullCommand(string Command)
+{
+ map<string,string>::iterator pFound=m_CommandCache.find(Command);
+ string OriCommand=Command;
+ if (pFound==m_CommandCache.end())
+ {
+ bool Found=false;
+ // Not found in the stack, search in the environment path
+ // Check if an extension is specified
+ const char *pBeg=Command.c_str();
+ const char *pEnd=pBeg+Command.length()-1;
+ bool HasExt=false;
+ while (pEnd>pBeg && *pEnd!='\\' && *pEnd!='/')
+ {
+ if (*pEnd=='.')
+ {
+ HasExt=true;
+ break;
+ }
+ pEnd--;
+ }
+ if (HasExt)
+ {
+ string FullCommand=SearchCommand(Command);
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ Command=FullCommand;
+ }
+ }
+ else
+ {
+ static bool s_Py2ExeInstalled=true;
+ /* First check for special internal commands */
+ if (OriCommand=="del")
+ {
+ m_CommandCache[OriCommand]="del";
+ return Command;
+ }
+ // Try with different extensions
+ string FullCommand=SearchCommand(Command,EXEEXT);
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ #ifdef WIN32
+ /* Check if a python script also exists, is so try generating the executable again. */
+ string PythonFullCommand=SearchCommand(Command,".py");
+ Command=FullCommand;
+ if (!PythonFullCommand.empty()&&s_Py2ExeInstalled)
+ {
+ fileinfo* pExeFile=GetFileInfo(FullCommand,m_MakeDir);
+ fileinfo* pPyFile=GetFileInfo(PythonFullCommand,m_MakeDir);
+ bool bBuild=false;
+ if (pExeFile->GetDate().IsOlder(pPyFile->GetDate()))
+ {
+ bBuild=true;
+ }
+ if (!bBuild)
+ {
+ deps_t Autodeps;
+ GetAutoDeps(pPyFile, Autodeps);
+ deps_t::iterator It=Autodeps.begin();
+ while (It!=Autodeps.end())
+ {
+ if (pExeFile->GetDate().IsOlder((*It)->GetDate()))
+ {
+ bBuild=true;
+ break;
+ }
+ It++;
+ }
+ }
+ if (bBuild)
+ {
+ if (pExeFile->Exists())
+ remove(pExeFile->GetFullFileName().c_str());
+ CreatePythonExe(PythonFullCommand);
+ // Invalidate the exe date since it could have been recreated by the CreatePythonExe
+ pExeFile->InvalidateDate();
+ }
+
+ }
+ #else
+ Command=FullCommand;
+ #endif
+ }
+ else
+ {
+ FullCommand=SearchCommand(Command,".py");
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ #ifdef WIN32
+ /* Now first try to create an executable for it */
+ if (s_Py2ExeInstalled)
+ {
+ fileinfo* pExeFile;
+ CreatePythonExe(FullCommand);
+ string ExeFullCommand=SearchCommand(Command,EXEEXT);
+ if (!ExeFullCommand.empty())
+ {
+ pExeFile=GetFileInfo(ExeFullCommand,m_MakeDir);
+ pExeFile->InvalidateDate(); // The file was just generated, make sure the correct date is taken.
+ }
+ if (ExeFullCommand.empty() || !pExeFile->Exists())
+ {
+ s_Py2ExeInstalled=false;
+ cout << "\nWarning: cannot convert "<<FullCommand<<".\nCompilation will be faster by installing py2exe.\n\n";
+ Command=GetPythonExe()+QuoteFileName(FullCommand);
+ }
+ else
+ Command=ExeFullCommand;
+ }
+ else
+ #endif
+ Command=GetPythonExe()+QuoteFileName(FullCommand);
+ }
+ }
+ }
+ if (!Found)
+ {
+ Command=GetComspec()+QuoteFileName(Command);
+ }
+ m_CommandCache[OriCommand]=Command;
+ return Command;
+ }
+ return pFound->second;
+}
+
+mh_pid_t mhmakefileparser::OsExeCommand(const string &Command, const string &Params, bool IgnoreError, string *pOutput) const
+{
+ string FullCommandLine;
+ string ComSpec=GetComspec();
+#ifdef WIN32
+ STARTUPINFO StartupInfo;
+ memset(&StartupInfo,0,sizeof(StartupInfo));
+ StartupInfo.cb=sizeof(STARTUPINFO);
+ PROCESS_INFORMATION ProcessInfo;
+
+ if (Command.substr(0,ComSpec.size())==ComSpec)
+ {
+ string tmpCommand=Command.substr(ComSpec.size(),Command.size());
+ FullCommandLine=ComSpec;
+ FullCommandLine+=g_QuoteString+QuoteFileName(tmpCommand)+Params+g_QuoteString;
+ }
+ else
+ {
+ const string PythonExe=GetPythonExe();
+ if (!(Command.substr(0,PythonExe.size())==PythonExe))
+ FullCommandLine=QuoteFileName(Command)+Params;
+ else
+ FullCommandLine=Command+Params;
+ }
+ char *pFullCommand=new char[FullCommandLine.length()+1];
+ strcpy(pFullCommand,FullCommandLine.c_str());
+
+ if (pOutput || g_Quiet)
+ {
+ mh_pid_t hChildStdinRd;
+ mh_pid_t hChildStdinWr;
+ mh_pid_t hChildStdoutRd;
+ mh_pid_t hChildStdoutWr;
+ mh_pid_t hChildStdinWrDup;
+ mh_pid_t hChildStdoutRdDup;
+ SECURITY_ATTRIBUTES saAttr;
+ BOOL fSuccess;
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+ return (mh_pid_t)-1;
+
+ /* Create new output read handle and the input write handle. Set
+ * the inheritance properties to FALSE. Otherwise, the child inherits
+ * the these handles; resulting in non-closeable handles to the pipes
+ * being created. */
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+ GetCurrentProcess(), &hChildStdinWrDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess) return (mh_pid_t)-1;
+ /* Close the inheritable version of ChildStdin that we're using. */
+ CloseHandle(hChildStdinWr);
+
+ if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+ return (mh_pid_t)-1;
+
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+ GetCurrentProcess(), &hChildStdoutRdDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess) return (mh_pid_t)-1;
+ CloseHandle(hChildStdoutRd);
+
+ int hStdIn = _open_osfhandle((long)hChildStdinWrDup, _O_WRONLY|_O_TEXT);
+ FILE *pStdIn = _fdopen(hStdIn, "w");
+ int hStdOut = _open_osfhandle((long)hChildStdoutRdDup, _O_RDONLY|_O_TEXT);
+ FILE *pStdOut = _fdopen(hStdOut, "r");
+
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+ StartupInfo.hStdInput = hChildStdinRd;
+ StartupInfo.hStdOutput = hChildStdoutWr;
+ StartupInfo.hStdError = hChildStdoutWr;
+
+ if (!CreateProcess(NULL,pFullCommand,NULL,NULL,TRUE,CREATE_NO_WINDOW,m_pEnv,m_MakeDir->GetFullFileName().c_str(),&StartupInfo,&ProcessInfo))
+ {
+ delete[] pFullCommand;
+ string ErrorMessage=string("Error starting command: ") + FullCommandLine + " : " + stringify(GetLastError());
+ if (IgnoreError)
+ cerr << ErrorMessage << endl;
+ else
+ throw ErrorMessage;
+ }
+ delete[] pFullCommand;
+ if (!CloseHandle(hChildStdinRd)) return (mh_pid_t)-1;
+ if (!CloseHandle(hChildStdoutWr)) return (mh_pid_t)-1;
+
+ CloseHandle(ProcessInfo.hThread);
+ char Buf[256];
+ size_t Nbr;
+ while ( (Nbr=fread(Buf,1,sizeof(Buf)-1,pStdOut)) > 0)
+ {
+ if (pOutput)
+ {
+ Buf[Nbr]=0;
+ *pOutput+=Buf;
+ }
+ }
+ WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+ fclose(pStdIn);
+ fclose(pStdOut);
+
+ DWORD ExitCode=0;
+ if (!GetExitCodeProcess(ProcessInfo.hProcess,&ExitCode) || ExitCode)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error running command: "<<Command<<", but ignoring error\n";
+ return (mh_pid_t)0; // Ignore error
+ }
+ else
+ return (mh_pid_t)-1;
+ }
+ CloseHandle(ProcessInfo.hProcess);
+ return (mh_pid_t)0;
+ }
+ else
+ {
+ if (!CreateProcess(NULL,pFullCommand,NULL,NULL,TRUE,0,m_pEnv,m_MakeDir->GetFullFileName().c_str(),&StartupInfo,&ProcessInfo))
+ {
+ delete[] pFullCommand;
+ string ErrorMessage=string("Error starting command: ") + Command + " : " + stringify(GetLastError());
+ if (IgnoreError)
+ cerr << ErrorMessage << endl;
+ else
+ throw ErrorMessage;
+ }
+ delete[] pFullCommand;
+ CloseHandle(ProcessInfo.hThread);
+ return ProcessInfo.hProcess;
+ }
+#else
+ if (Command.substr(0,ComSpec.size())==ComSpec)
+ {
+ string tmpCommand=Command.substr(ComSpec.size(),Command.size());
+ FullCommandLine=ComSpec;
+ FullCommandLine+=QuoteFileName(tmpCommand)+Params;
+ }
+ else
+ {
+ FullCommandLine=Command+Params;
+ }
+
+ if (pOutput || g_Quiet)
+ {
+ int pipeto[2]; /* pipe to feed the exec'ed program input */
+ int pipefrom[2]; /* pipe to get the exec'ed program output */
+
+ pipe( pipeto );
+ pipe( pipefrom );
+
+ pid_t ID=vfork();
+ if (ID==-1)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error forking when try to run command: "<<Command<<", but ignoring error\n";
+ return (mh_pid_t)0; // Ignore error
+ }
+ else
+ return (mh_pid_t)-1;
+ }
+ else if (ID==0)
+ {
+ int argc;
+ const char **pargv;
+
+ dup2( pipeto[0], STDIN_FILENO );
+ dup2( pipefrom[1], STDOUT_FILENO );
+ /* close unnecessary pipe descriptors for a clean environment */
+ close( pipeto[0] );
+ close( pipeto[1] );
+ close( pipefrom[0] );
+ close( pipefrom[1] );
+
+ poptParseArgvString(FullCommandLine.c_str(), &argc, &pargv);
+ chdir(m_MakeDir->GetFullFileName().c_str());
+ if (m_pEnv)
+ execve(pargv[0],(char *const*)pargv,m_pEnv);
+ else
+ execv(pargv[0],(char *const*)pargv);
+ free(pargv);
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ /* Close unused pipe ends. This is especially important for the
+ * pipefrom[1] write descriptor, otherwise readFromPipe will never
+ * get an EOF. */
+ close( pipeto[0] );
+ close( pipefrom[1] );
+
+ pid_t ID2=vfork();
+ if (ID2==-1)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error forking when try to run command: "<<Command<<", but ignoring error\n";
+ return (mh_pid_t)0; // Ignore error
+ }
+ else
+ return (mh_pid_t)-1;
+ }
+ else if (ID2==0)
+ {
+ /* Close pipe write descriptor, or we will never know when the
+ * writer process closes its end of the pipe and stops feeding the
+ * exec'ed program. */
+ close( pipeto[1] );
+ char Buf[256];
+ int Nbr;
+ while ( (Nbr=read(pipefrom[0],Buf,sizeof(Buf)-1)) > 0)
+ {
+ if (pOutput)
+ {
+ Buf[Nbr]=0;
+ *pOutput+=Buf;
+ }
+ }
+ close( pipefrom[0]);
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ /* close unused pipe end */
+ close( pipefrom[0] );
+ close( pipeto[1] );
+
+ int Status;
+ waitpid(ID2,&Status,0); // Wait until the reading of the output is finished
+ }
+
+ int Status;
+ int Ret=waitpid(ID,&Status,0);
+ if (Ret!=ID || Status)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error running command: "<<Command<<", but ignoring error\n";
+ return (mh_pid_t)0; // Ignore error
+ }
+ else
+ return (mh_pid_t)-1;
+ }
+ }
+ }
+ else
+ { // No pOutput
+ pid_t ID=fork();
+ if (ID==-1)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error forking when try to run command: "<<Command<<", but ignoring error\n";
+ return (mh_pid_t)0; // Ignore error
+ }
+ else
+ return (mh_pid_t)-1;
+ }
+ else if (ID==0)
+ {
+ int argc;
+ const char **pargv;
+
+ poptParseArgvString(FullCommandLine.c_str(), &argc, &pargv);
+ chdir(m_MakeDir->GetFullFileName().c_str());
+ if (m_pEnv)
+ execve(pargv[0],(char *const*)pargv,m_pEnv);
+ else
+ execv(pargv[0],(char *const*)pargv);
+ free(pargv);
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ return (mh_pid_t)ID;
+ }
+ }
+
+ return (mh_pid_t)0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#ifndef WIN32
+string EscapeQuotes(const string &Params)
+{
+ int OldPos=0;
+ int Pos;
+ string Quote("\\\"");
+ string SemiColon(" ; ");
+ string Ret;
+
+ while (1)
+ {
+ int Pos=Params.find_first_of('"',OldPos);
+ int Pos2=Params.find(" & ",OldPos);
+ string ToReplace(Quote);
+ int Inc=1;
+
+ if (Pos==string::npos)
+ {
+ if (Pos2==string::npos)
+ break;
+ Pos=Pos2;
+ ToReplace=SemiColon;
+ Inc=3;
+ }
+ else
+ {
+ if (Pos2!=string::npos && Pos2<Pos)
+ {
+ Pos=Pos2;
+ ToReplace=SemiColon;
+ Inc=3;
+ }
+ }
+ Ret+=Params.substr(OldPos,Pos-OldPos);
+ Ret+=ToReplace;
+ OldPos=Pos+Inc;
+ }
+ Ret+=Params.substr(OldPos);
+ return Ret;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+mh_pid_t mhmakefileparser::ExecuteCommand(string Command, bool &IgnoreError, string *pOutput)
+{
+ bool Echo=true;
+ IgnoreError=false;
+ while (Command.length())
+ {
+ if (Command[0]=='@')
+ {
+ Echo=false;
+ Command=Command.substr(1);
+ continue;
+ }
+ if (Command[0]=='-')
+ {
+ IgnoreError=true;
+ Command=Command.substr(1);
+ continue;
+ }
+ break;
+ }
+ if (!Command.length())
+ (mh_pid_t)0;
+ string InCommand=Command;
+ if (g_Quiet)
+ Echo=false;
+
+ const char *pCom=Command.c_str();
+ int StartCommandPos;
+ int EndCommandPos;
+ int BeginParamPos;
+ if (*pCom=='"')
+ {
+ StartCommandPos=1;
+ EndCommandPos=1;
+ while (pCom[EndCommandPos]!='"') EndCommandPos++;
+ }
+ else
+ {
+ StartCommandPos=0;
+ EndCommandPos=0;
+ }
+ while (!strchr(" \t",pCom[EndCommandPos])) EndCommandPos++;
+ BeginParamPos=EndCommandPos;
+ string Params=Command.substr(BeginParamPos);
+ Command=Command.substr(StartCommandPos,EndCommandPos-StartCommandPos);
+
+ // If we have special characters in the params we always call the command via the shell
+ unsigned i;
+ for (i=0; i<Params.size(); i++)
+ {
+ if (strchr("<>|&",Params[i]))
+ {
+ break;
+ }
+ }
+ if (i==Params.size())
+ {
+ if (Command!="del" && Command!="touch" && Command!="copy" && Command!="echo")
+ Command=GetFullCommand(Command);
+#ifndef WIN32
+ if (Command.substr(0,GetComspec().size())==GetComspec())
+ {
+ Params=EscapeQuotes(Params);
+ Params+="\"";
+ }
+#endif
+ }
+ else
+ {
+ if (Command!="echo" || strchr(Params.c_str(),'|')) // the EchoCommand(Params) does not implement piping, only redirecting (>)
+ {
+ string FullCommand=GetFullCommand(Command);
+ string ComSpec=GetComspec();
+ if (FullCommand.substr(0,ComSpec.size())!=ComSpec)
+ Command=FullCommand; // Only use FullCommand when it was found and not prepending by the comspec.
+ Command=ComSpec+Command;
+#ifndef WIN32
+ Params=EscapeQuotes(Params);
+ Params+="\"";
+#endif
+ }
+ }
+ if (Echo
+ #ifdef _DEBUG
+ || g_DoNotExecute
+ #endif
+ )
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << Command << Params << endl;
+ else
+ cout << InCommand << endl;
+ #endif
+ }
+
+ /* first we check special internal commands */
+ #ifdef _DEBUG
+ if (pOutput || !g_DoNotExecute)
+ {
+ #endif
+ if (Command=="del")
+ {
+ return DeleteFiles(Params);
+ }
+ else if (Command=="touch")
+ {
+ return TouchFiles(Params);
+ }
+ else if (Command=="copy")
+ {
+ return CopyFiles(Params);
+ }
+ else if (Command=="echo")
+ {
+ return EchoCommand(Params);
+ }
+
+ return OsExeCommand(Command,Params,IgnoreError,pOutput);
+ #ifdef _DEBUG
+ }
+ #endif
+ return (mh_pid_t)0; /* No Error */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::BuildDependencies(const refptr<rule> &pRule, fileinfo* pTarget, mh_time_t TargetDate, mh_time_t &YoungestDate, bool &MakeTarget)
+{
+
+ vector<fileinfo*> &Deps=pTarget->GetDeps();
+ vector<fileinfo*>::iterator pDepIt=Deps.begin();
+ while (pDepIt!=Deps.end())
+ {
+ StartBuildTarget(*pDepIt);
+ pDepIt++;
+ }
+ pDepIt=Deps.begin();
+ while (pDepIt!=Deps.end())
+ {
+ mh_time_t DepDate=WaitBuildTarget(*pDepIt);
+ #ifdef _DEBUG
+ if (!DepDate.DoesExist()&&!(*pDepIt)->IsPhony())
+ {
+ if (!(*pDepIt)->GetDir()->GetDate().DoesExist())
+ cout << "Building of "<<(*pDepIt)->GetFullFileName()<<" failed because directory does not exist (rule defined to create it?)\n";
+ else
+ cout << "Does not known how to create "<<(*pDepIt)->GetFullFileName()<<".\n";
+ }
+ #endif
+ if (DepDate.IsNewer(YoungestDate))
+ YoungestDate=DepDate;
+ if (DepDate.IsNewer(TargetDate))
+ {
+ #ifdef _DEBUG
+ if (pRule&&g_pPrintDependencyCheck && DepDate.IsExistingFile() && TargetDate.IsExistingFile())
+ cout<<"Going to build "<<pTarget->GetQuotedFullFileName()<<" because "<<(*pDepIt)->GetQuotedFullFileName()<<" is more recent\n";
+ #endif
+ MakeTarget=true;
+ }
+ pDepIt++;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+mh_time_t mhmakefileparser::StartBuildTarget(fileinfo* pTarget,bool bCheckTargetDir)
+{
+ #ifdef _DEBUG
+ if (g_CheckCircularDeps)
+ {
+ deque<fileinfo*>::const_iterator pFind=find(m_TargetStack.begin(),m_TargetStack.end(),pTarget);
+ if (pFind!=m_TargetStack.end())
+ {
+ cout << "Circular dependency detected.\n"<<pTarget->GetQuotedFullFileName()<<" depending on itself.\n";
+ cout << "Dependency stack:\n";
+ deque<fileinfo*>::const_iterator pIt=m_TargetStack.begin();
+ while (pIt!=m_TargetStack.end())
+ {
+ cout << " " << (*pIt)->GetQuotedFullFileName() << endl;
+ pIt++;
+ }
+ }
+ if (!pTarget->IsBuildStarted()) m_TargetStack.push_back(pTarget);
+ }
+ #endif
+
+ #ifdef _DEBUG
+ static int Indent;
+ #endif
+
+ if (g_StopCompiling)
+ {
+ throw string("Compilation Interrupted by user.");
+ }
+
+ if (pTarget->IsBuild())
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<" Already build "<<pTarget->GetQuotedFullFileName()<<" : "<<pTarget->GetDate()<<endl;
+ }
+ #endif
+ return pTarget->GetDate();
+ }
+ if (pTarget->IsBuilding())
+ return mh_time_t(); // pTarget is still building, so we have to wait
+
+ #ifdef _DEBUG
+ if (g_GenProjectTree)
+ cout << pTarget->GetQuotedFullFileName() << endl;
+
+ Indent++;
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building dependencies of "<<pTarget->GetQuotedFullFileName()<<endl;
+ }
+ #endif
+
+ pTarget->SetBuild();
+
+ /* Optimisation: do not build target when target dir does not exist,
+ but first build the target dir, in case there exists a rule for it*/
+ refptr<rule> pRule=pTarget->GetRule();
+
+ if (!pRule && bCheckTargetDir)
+ {
+ fileinfo *pTargetDir=pTarget->GetDir();
+ mh_time_t TargetDirDate=BuildTarget(pTargetDir,false);
+
+ if (!pTargetDir->Exists())
+ {
+ #ifdef _DEBUG
+ Indent--;
+ if (g_CheckCircularDeps)
+ {
+ m_TargetStack.pop_back();
+ }
+ #endif
+ return TargetDirDate;
+ }
+ }
+
+ mh_time_t TargetDate=pTarget->GetDate();
+ bool MakeTarget=false;
+ mh_time_t YoungestDate=TargetDate;
+
+ if (!pRule || !pRule->GetCommands().size())
+ {
+ implicitruledep_t Result;
+
+ IMPLICITRULE::SearchImplicitRule(pTarget,Result);
+
+ implicitruledep_t::iterator ResultIt=Result.begin();
+ while (ResultIt!=Result.end())
+ {
+ if (ResultIt->first.empty())
+ {
+ pRule=ResultIt->second;
+ pTarget->SetRule(pRule);
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Found implicit rule for "<<pTarget->GetQuotedFullFileName()<<endl;
+ pRule->PrintCommands(pTarget);
+ }
+ #endif
+ break;
+ }
+ else if (!IMPLICITRULE::PushRule(ResultIt->second))
+ {
+ /* We have to check all the implicitrule dependencies, only when they all can be build,
+ the implicit rule matches */
+ #ifdef _DEBUG
+ fileinfo *pYoungestTarget;
+ m_ImplicitSearch++;
+ #endif
+ mh_time_t ThisYoungestDate=YoungestDate;
+ vector<fileinfo*>::iterator ImplicitRuleDepsIt=ResultIt->first.begin();
+ while (ImplicitRuleDepsIt!=ResultIt->first.end())
+ {
+ fileinfo *pNewTarget=*ImplicitRuleDepsIt;
+ mh_time_t DepDate=BuildTarget(pNewTarget);
+ if (!DepDate.DoesExist())
+ {
+ pNewTarget=ResultIt->second->GetMakefile()->SearchvPath(pNewTarget);
+ if (pNewTarget!=NULL)
+ {
+ *ImplicitRuleDepsIt=pNewTarget; /* The file was elsewere so we have to update the dependencies */
+ DepDate=pNewTarget->GetDate();
+ }
+ }
+ if (!DepDate.DoesExist())
+ break;
+ if (DepDate.IsNewer(ThisYoungestDate))
+ {
+ ThisYoungestDate=DepDate;
+ #ifdef _DEBUG
+ pYoungestTarget=pNewTarget;
+ #endif
+ }
+ ImplicitRuleDepsIt++;
+ }
+
+ IMPLICITRULE::PopRule(ResultIt->second);
+ #ifdef _DEBUG
+ m_ImplicitSearch--;
+ #endif
+ if (ImplicitRuleDepsIt==ResultIt->first.end()) // All deps exists
+ {
+ ThisYoungestDate=YoungestDate;
+ pRule=ResultIt->second;
+ pTarget->InsertDeps(ResultIt->first);
+ pTarget->SetRule(pRule); /* This is an implicit rule so do not add the target */
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Found implicit rule for "<<pTarget->GetQuotedFullFileName()<<". Dependents:\n";
+ vector<fileinfo*>::iterator ImplicitRuleDepsIt=ResultIt->first.begin();
+ while (ImplicitRuleDepsIt!=ResultIt->first.end())
+ {
+ cout<<" "<<(*ImplicitRuleDepsIt)->GetQuotedFullFileName()<<endl;
+ ImplicitRuleDepsIt++;
+ }
+ pRule->PrintCommands(pTarget);
+ }
+ #endif
+ if (ThisYoungestDate.IsNewer(TargetDate))
+ {
+ #ifdef _DEBUG
+ if (pRule,g_pPrintDependencyCheck && ThisYoungestDate.IsExistingFile() && TargetDate.IsExistingFile())
+ cout<<"Going to build "<<pTarget->GetQuotedFullFileName()<<" because "<<pYoungestTarget->GetQuotedFullFileName()<<" is more recent\n";
+ #endif
+ MakeTarget=true;
+ }
+ break;
+ }
+ }
+ #ifdef _DEBUG
+ else if (g_PrintAdditionalInfo)
+ {
+ cout << "Stopping implicit search of "<<pTarget->GetFullFileName()<<" because of implicit rule recursion detected.\n";
+ }
+ #endif
+ ResultIt++;
+ }
+ if (pRule)
+ {
+ #ifdef _DEBUG
+ pTarget->SetBuilding();
+ pTarget->SetRule(pRule);
+ pTarget->ClearBuilding();
+ #else
+ pTarget->SetRule(pRule);
+ #endif
+ }
+ }
+
+ mhmakefileparser *pMakefile=NULL;
+ if (pRule)
+ {
+ pMakefile=pRule->GetMakefile();
+ if (pMakefile->ForceAutoDepRescan()||MakeTarget==true)
+ pMakefile->UpdateAutomaticDependencies(pTarget);
+ }
+ else if (pTarget->GetAutoDepsMakefile())
+ pTarget->GetAutoDepsMakefile()->UpdateNoRuleAutomaticDependencies(pTarget);
+
+ BuildDependencies(pRule,pTarget,TargetDate,YoungestDate,MakeTarget);
+
+ if (pRule)
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building "<<pTarget->GetQuotedFullFileName()<<endl;
+ }
+ #endif
+ if (!MakeTarget)
+ {
+ if (!TargetDate.DoesExist() || ( (g_RebuildAll || pMakefile->m_RebuildAll) && TargetDate.IsOlder(m_sBuildTime)))
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ if (!TargetDate.DoesExist())
+ {
+ if (!m_ImplicitSearch && !pTarget->IsPhony())
+ cout<<"Building "<<pTarget->GetQuotedFullFileName()<<" because it does not exist yet\n";
+ }
+ else if (TargetDate.IsOlder(m_sBuildTime))
+ {
+ cout<<"Building "<<pTarget->GetQuotedFullFileName()<<" because need to rebuild all (-a)\n";
+ }
+ }
+ #endif
+ MakeTarget=true;
+ }
+ }
+
+ // Now execute the commands
+ vector<string> &Commands=pRule->GetCommands();
+
+ if (!MakeTarget)
+ {
+ vector<string>::iterator CommandIt=Commands.begin();
+
+ md5_context ctx;
+ md5_starts( &ctx );
+ while (CommandIt!=Commands.end())
+ {
+ pMakefile->SetRuleThatIsBuild(pTarget); // Make sure that the command expension is correct
+ string Command=pMakefile->ExpandExpression(*CommandIt);
+ pMakefile->ClearRuleThatIsBuild(); /* Make sure that further expansion is not taking this rule into account.*/
+ md5_update( &ctx, (uint8 *)Command.c_str(), (unsigned long)Command.size());
+ CommandIt++;
+ }
+
+ uint32 Md5_32=md5_finish32( &ctx);
+ if (!pTarget->CompareMd5_32(Md5_32))
+ {
+ if (TargetDate.IsNewerOrSame(m_sBuildTime) || TargetDate.IsDir())
+ {
+ // Only rebuild if it is not yet rebuild in this current run. This may happen for implicit rules that have multiple targets (implicit rules that build more then one target at the same time
+ pTarget->SetCommandsMd5_32(Md5_32);
+ pMakefile->AddTarget(pTarget);
+ pMakefile->SetAutoDepsDirty(); /* We need to update the autodeps file if the md5 has been changed */
+ }
+ else
+ {
+ #ifdef _DEBUG
+ if (!g_GenProjectTree)
+ cout << "Md5 is different for " << pTarget->GetQuotedFullFileName() << " Old:"<<hex<<pTarget->GetCommandsMd5_32()<<", New: "<<Md5_32<<". Commandline must have been changed so recompiling\n";
+ #endif
+
+ #ifdef _DEBUG
+ Indent--;
+ if (g_CheckCircularDeps)
+ {
+ m_TargetStack.pop_back();
+ }
+ #endif
+
+ MakeTarget=true;
+ }
+ }
+ }
+ if (MakeTarget)
+ {
+ // Queue for execution
+ if (sm_CommandQueue.QueueTarget(pTarget))
+ return mh_time_t();
+ mh_time_t NewDate=pTarget->GetDate();
+ if (NewDate.IsNewer(YoungestDate))
+ YoungestDate=NewDate;
+ }
+ }
+
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building "<<pTarget->GetQuotedFullFileName()<<" finished : "<< YoungestDate << endl;
+ }
+ Indent--;
+ if (g_CheckCircularDeps)
+ {
+ m_TargetStack.pop_back();
+ }
+
+ if (!m_ImplicitSearch && !pTarget->Exists() && !pTarget->IsPhony() && !g_DoNotExecute && !g_GenProjectTree)
+ {
+ // This is only a warning for phony messages
+ cout<<"Warning: don't know how to make "<<pTarget->GetQuotedFullFileName()<<"\nMake the rule a phony rule to avoid this warning (but only do it when it is really phony).\n";;
+ }
+ #endif
+ pTarget->SetDate(YoungestDate); /* This is especially needed for phony targets in between real targets */
+ return YoungestDate;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+mh_time_t mhmakefileparser::WaitBuildTarget(fileinfo* pTarget)
+{
+ return sm_CommandQueue.WaitForTarget(pTarget);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::BuildIncludedMakefiles()
+{
+ vector<fileinfo*>::iterator MakefileIt=m_IncludedMakefiles.begin();
+ while (MakefileIt!=m_IncludedMakefiles.end())
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout<<"Building include file "<<(*MakefileIt)->GetQuotedFullFileName()<<endl;
+ #endif
+ BuildTarget(*MakefileIt);
+ MakefileIt++;
+ }
+}
diff --git a/tools/mhmake/src/commandqueue.cpp b/tools/mhmake/src/commandqueue.cpp
new file mode 100644
index 000000000..6f3fd4d1a
--- /dev/null
+++ b/tools/mhmake/src/commandqueue.cpp
@@ -0,0 +1,410 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "commandqueue.h"
+#include "mhmakeparser.h"
+
+#ifndef WIN32
+
+#define INFINITE 0
+#define FALSE 0
+
+static int Status;
+unsigned WaitForMultipleObjects(int Nbr, mh_pid_t *phProcesses, int, int)
+{
+ mh_pid_t ID=waitpid(0,&Status,0);
+ for (int i=0; i<Nbr; i++)
+ {
+ if (ID==phProcesses[i])
+ return i;
+ }
+ return -1;
+}
+
+typedef unsigned DWORD;
+
+int GetExitCodeProcess(mh_pid_t hProcess, DWORD *pExitCode)
+{
+ *pExitCode=Status;
+ return true;
+}
+
+#define CloseHandle(Handle)
+
+#endif
+
+
+commandqueue::commandqueue() :
+ m_NrActiveEntries(0)
+{
+#ifdef WIN32
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ m_MaxNrCommandsInParallel=SysInfo.dwNumberOfProcessors;
+
+ m_DummyWaitHandle=(mh_pid_t)CreateEvent(NULL,TRUE,FALSE,NULL);
+#else
+ FILE *pFile=fopen("/proc/cpuinfo","r");
+ const char *pProc="\nprocessor";
+ int cur=1;
+ int NrProcs=0;
+ while (!feof(pFile))
+ {
+ char In=fgetc(pFile);
+ if (In==pProc[cur])
+ {
+ cur++;
+ if (!pProc[cur])
+ {
+ NrProcs++;
+ cur=0;
+ }
+ }
+ else
+ cur=0;
+ }
+ m_MaxNrCommandsInParallel=NrProcs;
+ m_DummyWaitHandle=(mh_pid_t)-1;
+
+#endif
+
+ m_pActiveProcesses=new mh_pid_t[m_MaxNrCommandsInParallel];
+ m_pActiveEntries= new refptr<activeentry>[m_MaxNrCommandsInParallel];
+}
+
+commandqueue::~commandqueue()
+{
+ delete [] m_pActiveProcesses;
+ delete [] m_pActiveEntries;
+ #ifdef WIN32
+ CloseHandle(m_DummyWaitHandle);
+ #endif
+}
+
+void commandqueue::SetNrParallelBuilds(unsigned NrParallelBuilds)
+{
+ if (m_NrActiveEntries)
+ throw("Changing of number of parallel builds is only allowed when no commands are executing");
+ NrParallelBuilds=max(1u,NrParallelBuilds);
+ m_MaxNrCommandsInParallel=NrParallelBuilds;
+ delete [] m_pActiveProcesses;
+ delete [] m_pActiveEntries;
+ m_pActiveProcesses=new mh_pid_t[NrParallelBuilds];
+ m_pActiveEntries= new refptr<activeentry>[NrParallelBuilds];
+}
+
+void commandqueue::ThrowCommandExecutionError(refptr<activeentry> pActiveEntry)
+{
+ fileinfo* pTarget=pActiveEntry->pTarget;
+ const string &Command=pActiveEntry->Command;
+ mhmakefileparser *pMakefile=pTarget->GetRule()->GetMakefile();
+
+ string ErrorMessage = string("Error running command: ")+ Command +"\n";
+ ErrorMessage += "Command defined in makefile: " + pMakefile->GetMakeDir()->GetQuotedFullFileName();
+ pTarget->SetCommandsMd5_32(0); /* Clear the md5 to make sure that the target is rebuild the next time mhmake is ran */
+ pMakefile->SetAutoDepsDirty(); /* We need to update the autodeps file if the md5 has been changed */
+ throw ErrorMessage;
+}
+
+refptr<commandqueue::activeentry> commandqueue::CreateActiveEntry(void)
+{
+ refptr<activeentry> pRet=new activeentry;
+ m_pActiveEntries[m_NrActiveEntries]=pRet;
+ m_pActiveProcesses[m_NrActiveEntries]=this->m_DummyWaitHandle;
+ m_NrActiveEntries++;
+ return pRet;
+}
+
+unsigned commandqueue::GetActiveEntryId(const refptr<activeentry> pActiveEntry) const
+{
+ unsigned i=0;
+ for (i=0; i<m_NrActiveEntries; i++)
+ if (m_pActiveEntries[i]==pActiveEntry)
+ return i;
+ throw("ActiveEntry not found for "+ pActiveEntry->pTarget->GetFullFileName());
+}
+
+void commandqueue::RemoveActiveEntry(unsigned Entry)
+{
+//cout << "Remove entry "<<Entry<<" of "<<m_NrActiveEntries<<" from queue:"<<m_pActiveEntries[Entry].pTarget->GetQuotedFullFileName()<<<<" ("<<m_pActiveEntries[Entry]<<")\n";
+ unsigned NrToMove=m_NrActiveEntries-Entry-1;
+ if (NrToMove)
+ {
+ for (unsigned i=0; i<NrToMove; i++)
+ {
+ unsigned EntryP1=Entry+1;
+ m_pActiveEntries[Entry]=m_pActiveEntries[EntryP1];
+ m_pActiveProcesses[Entry]=m_pActiveProcesses[EntryP1];
+ Entry=EntryP1;
+ }
+ }
+ m_pActiveEntries[Entry]=NULL;
+ m_pActiveProcesses[Entry]=NULL;
+ m_NrActiveEntries--;
+}
+
+/* Start to execute next command, return true when command is completely executed
+ upon return */
+bool commandqueue::StartExecuteNextCommand(refptr<activeentry> pActiveEntry, mh_pid_t *pActiveProcess)
+{
+ fileinfo* pTarget=pActiveEntry->pTarget;
+ mhmakefileparser *pMakefile=pTarget->GetRule()->GetMakefile();
+
+ pMakefile->SetRuleThatIsBuild(pTarget); // Make sure that the command expension is correct
+ string Command=pMakefile->ExpandExpression(*pActiveEntry->CurrentCommandIt);
+ pMakefile->ClearRuleThatIsBuild(); /* Make sure that further expansion is not taking this rule into account.*/
+ md5_update( &pActiveEntry->md5ctx, (uint8 *)Command.c_str(), (unsigned long)Command.size());
+
+ pActiveEntry->Command=Command;
+
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ cout<<"-> "<<Command<<endl;
+ }
+ if (!g_GenProjectTree)
+ {
+ #endif
+ mh_pid_t hProcess=pMakefile->ExecuteCommand(Command,pActiveEntry->IgnoreError);
+ if (hProcess==(mh_pid_t)-1)
+ {
+ ThrowCommandExecutionError(pActiveEntry);
+ }
+ else if (!hProcess)
+ {
+ // Command already finished, so execute next command
+ return true;
+ }
+ else
+ {
+ // Command still executing, so add it to the list of handles to wait for
+ *pActiveProcess=hProcess;
+ return false;
+ }
+ #ifdef _DEBUG
+ }
+ #endif
+ return true;
+}
+
+void commandqueue::TargetBuildFinished(refptr<activeentry> pActiveEntry)
+{
+ fileinfo* pTarget=pActiveEntry->pTarget;
+
+ // Building of this target finished
+ uint32 Md5_32=md5_finish32( &pActiveEntry->md5ctx);
+ #ifdef _DEBUG
+ if (g_DoNotExecute||g_GenProjectTree)
+ pTarget->SetDateToNow();
+ else
+ #endif
+ pTarget->InvalidateDate();
+
+ pTarget->SetCommandsMd5_32(Md5_32); /* If the rule of the target was added with an implicit rule the targets in the rule is empty */
+
+ refptr<rule> pRule=pTarget->GetRule();
+ mhmakefileparser *pMakefile=pRule->GetMakefile();
+
+ pMakefile->AddTarget(pTarget);
+ pMakefile->SetAutoDepsDirty();
+ pRule->SetTargetsIsBuild(Md5_32);
+
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ cout<<"Building "<<pTarget->GetQuotedFullFileName()<<" finished : "<< pTarget->GetDate() << endl;
+ }
+
+ if (!pMakefile->ImplicitSearch() && !pTarget->Exists() && !pTarget->IsPhony() && !g_DoNotExecute && !g_GenProjectTree)
+ {
+ // This is only a warning for phony messages
+ cout<<"Warning: don't know how to make "<<pTarget->GetQuotedFullFileName()<<"\nMake the rule a phony rule to avoid this warning (but only do it when it is really phony).\n";;
+ }
+ #endif
+
+ pTarget->ClearBuilding();
+}
+
+/* Start executing the commands of a target
+*/
+bool commandqueue::StartExecuteCommands(fileinfo* pTarget)
+{
+ cout << "Building " << pTarget->GetQuotedFullFileName()<<endl;
+ // We do not have to put it in the queue, we can start executing directly
+ refptr<rule> pRule=pTarget->GetRule();
+ mhmakefileparser *pMakefile=pRule->GetMakefile();
+ vector<string>::iterator CommandIt=pRule->GetCommands().begin();
+
+ refptr<activeentry> pActiveEntry=CreateActiveEntry();
+ mh_pid_t ActiveProcess;
+
+ md5_starts( &pActiveEntry->md5ctx );
+
+ pActiveEntry->pTarget=pTarget;
+ pActiveEntry->CurrentCommandIt=CommandIt;
+
+ while (1)
+ {
+ if (StartExecuteNextCommand(pActiveEntry, &ActiveProcess))
+ {
+ pActiveEntry->CurrentCommandIt++;
+ if (pActiveEntry->CurrentCommandIt==pRule->GetCommands().end())
+ {
+ // All commands executed
+ break;
+ }
+ }
+ else
+ {
+ m_pActiveProcesses[GetActiveEntryId(pActiveEntry)]=ActiveProcess; // We use GetActiveEntryId to avoid reentrancy problems
+ return false;
+ }
+ }
+ TargetBuildFinished(pActiveEntry);
+ RemoveActiveEntry(pActiveEntry);
+ return true;
+}
+
+
+/* put the target in the execution queue or start executing immediately
+*/
+bool commandqueue::QueueTarget(fileinfo* pTarget)
+{
+ pTarget->SetBuilding();
+ // First check if there is place in the active entries
+ if (m_NrActiveEntries==m_MaxNrCommandsInParallel)
+ {
+ // commands cannot be started yet
+ m_Queue.push(pTarget);
+ return true;
+ }
+ else
+ {
+ return !StartExecuteCommands(pTarget);
+ }
+}
+
+/* Wait for all the commands being executed of a target. In the mean time also continue
+ executing all other commands in the queue
+*/
+mh_time_t commandqueue::WaitForTarget(fileinfo *pTarget)
+{
+ if (!pTarget->IsBuilding())
+ return pTarget->GetDate();
+
+ while (1)
+ {
+ // First wait until one of the processes that are running is finished
+ if (m_NrActiveEntries==1 && m_pActiveProcesses[0]==this->m_DummyWaitHandle)
+ {
+ #ifdef _DEBUG
+ if (pTarget!=m_pActiveEntries[0]->pTarget)
+ throw("Wrong assumption: waiting for target " + pTarget->GetFullFileName() + " but in wait list is " + m_pActiveEntries[0]->pTarget->GetFullFileName());
+ #endif
+ pTarget->SetDateToNow();
+ return pTarget->GetDate(); // This is a reentrancy problem, assume that the target is just build
+ }
+ unsigned Ret=WaitForMultipleObjects(m_NrActiveEntries,m_pActiveProcesses,FALSE,INFINITE);
+ if (Ret>=m_NrActiveEntries)
+ #ifdef WIN32
+ throw("fatal error: unexpected return value of WaitForMultipleObjects " + stringify(Ret) + " " + stringify(GetLastError()));
+ #else
+ throw("fatal error: unexpected return value of WaitForMultipleObjects " + stringify(Ret));
+ #endif
+ refptr<activeentry> pActiveEntry=m_pActiveEntries[Ret];
+ fileinfo* pCurrentTarget=pActiveEntry->pTarget;
+ refptr<rule> pRule=pCurrentTarget->GetRule();
+
+ // First check the error code of the command
+ DWORD ExitCode=0;
+ mh_pid_t hProcess=m_pActiveProcesses[Ret];
+ if (!GetExitCodeProcess(hProcess,&ExitCode) || ExitCode)
+ {
+ if (pActiveEntry->IgnoreError)
+ {
+ cerr << "Error running command: "<<pActiveEntry->Command<<", but ignoring error\n";
+ }
+ else
+ ThrowCommandExecutionError(pActiveEntry);
+ }
+ CloseHandle(hProcess);
+
+ // Now check if we have to execute more commands for this target
+ pActiveEntry->CurrentCommandIt++;
+ while (pActiveEntry->CurrentCommandIt!=pRule->GetCommands().end())
+ {
+ if (!StartExecuteNextCommand(pActiveEntry, &m_pActiveProcesses[Ret]))
+ break; // We have to wait for end of command execution
+ pActiveEntry->CurrentCommandIt++;
+ }
+
+ if (pActiveEntry->CurrentCommandIt==pRule->GetCommands().end())
+ {
+ TargetBuildFinished(pActiveEntry);
+ // Target building finished, so remove it from the list of active entries
+ RemoveActiveEntry(Ret);
+
+ bool Return=false;
+ if (pTarget==pCurrentTarget)
+ {
+ Return = true;
+ }
+ // Check if we have other entries in the queue to start
+ while (1)
+ {
+ if (m_Queue.empty())
+ {
+ // There should still be active entries, otherwise this is a serious bug
+ if (!m_NrActiveEntries && !Return)
+ {
+ // This may happen when having multiple target rules and pTarget was build when on of the other targets
+ // was build. so first check if the pTarget was not build in the mean time. If so, this is not an error
+ if (pTarget->IsBuild())
+ {
+ Return=true;
+ break;
+ }
+ else
+ throw("Fatal error: WaitForTarget "+pTarget->GetQuotedFullFileName()+": no active targets anymore.");
+ }
+ else
+ break;
+ }
+ else
+ {
+ fileinfo* pNewTarget=m_Queue.front();
+ m_Queue.pop();
+ if (StartExecuteCommands(pNewTarget))
+ {
+ if (pNewTarget==pTarget)
+ Return=true;
+ }
+ else
+ break;
+ }
+ }
+ if (Return)
+ return pTarget->GetDate();
+ }
+ }
+
+}
diff --git a/tools/mhmake/src/commandqueue.h b/tools/mhmake/src/commandqueue.h
new file mode 100644
index 000000000..8b327be8c
--- /dev/null
+++ b/tools/mhmake/src/commandqueue.h
@@ -0,0 +1,73 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __COMMANDQUEUE_H__
+#define __COMMANDQUEUE_H__
+
+#include "fileinfo.h"
+
+#ifdef WIN32
+typedef HANDLE mh_pid_t;
+#else
+typedef pid_t mh_pid_t;
+#endif
+
+class commandqueue
+{
+ struct activeentry : public refbase
+ {
+ fileinfo* pTarget;
+ vector<string>::const_iterator CurrentCommandIt;
+ string Command;
+ md5_context md5ctx;
+ bool IgnoreError;
+ };
+private:
+ queue<fileinfo*> m_Queue;
+ unsigned m_MaxNrCommandsInParallel;
+ mh_pid_t *m_pActiveProcesses;
+ refptr<activeentry> *m_pActiveEntries;
+ unsigned m_NrActiveEntries;
+ mh_pid_t m_DummyWaitHandle;
+
+private:
+ void ThrowCommandExecutionError(refptr<activeentry> pActiveEntry);
+ refptr<activeentry> CreateActiveEntry(void);
+ unsigned GetActiveEntryId(const refptr<activeentry> pActiveEntry) const;
+ void RemoveActiveEntry(unsigned Entry);
+ void RemoveActiveEntry(refptr<activeentry> pActiveEntry)
+ {
+ RemoveActiveEntry(GetActiveEntryId(pActiveEntry));
+ }
+ bool StartExecuteCommands(fileinfo *pTarget);
+ bool StartExecuteNextCommand(refptr<activeentry> pActiveEntry, mh_pid_t *pActiveProcess);
+ void TargetBuildFinished(refptr<activeentry> pActiveEntry);
+
+public:
+ commandqueue();
+ ~commandqueue();
+
+ bool QueueTarget(fileinfo *pTarget); // Returns true if target has been queued, false when commands are executed upon return
+ mh_time_t WaitForTarget(fileinfo *pTarget);
+ void SetNrParallelBuilds(unsigned NrParallelBuilds);
+};
+
+
+#endif
diff --git a/tools/mhmake/src/curdir.cpp b/tools/mhmake/src/curdir.cpp
new file mode 100644
index 000000000..4b905a327
--- /dev/null
+++ b/tools/mhmake/src/curdir.cpp
@@ -0,0 +1,37 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "curdir.h"
+#include "util.h"
+
+fileinfos g_FileInfos; // declare here since it is important that it is constructed before m_pcurrentdir
+curdir::initcurdir curdir::m_pCurrentDir;
+
+///////////////////////////////////////////////////////////////////////////////
+curdir::initcurdir::initcurdir()
+{
+ char CurDir[MAX_PATH];
+ getcwd(CurDir,MAX_PATH);
+ string strCurDir=CurDir;
+ m_pDir=GetAbsFileInfo(NormalizePathName(strCurDir));
+}
diff --git a/tools/mhmake/src/curdir.h b/tools/mhmake/src/curdir.h
new file mode 100644
index 000000000..138f8f45a
--- /dev/null
+++ b/tools/mhmake/src/curdir.h
@@ -0,0 +1,47 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __CURDIR_H
+#define __CURDIR_H
+
+class fileinfo;
+
+class curdir
+{
+public:
+ class initcurdir
+ {
+ friend class curdir;
+ const fileinfo *m_pDir;
+ public:
+ initcurdir();
+ };
+private:
+ static initcurdir m_pCurrentDir;
+
+public:
+ static const fileinfo* GetCurDir()
+ {
+ return m_pCurrentDir.m_pDir;
+ }
+};
+
+#endif
+
diff --git a/tools/mhmake/src/fileinfo.cpp b/tools/mhmake/src/fileinfo.cpp
new file mode 100644
index 000000000..7174c49a4
--- /dev/null
+++ b/tools/mhmake/src/fileinfo.cpp
@@ -0,0 +1,363 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+#ifndef S_ISDIR
+#define S_ISDIR(val) ((val)&_S_IFDIR)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+string QuoteFileName(const string &Filename)
+{
+ string Ret(Filename);
+#if OSPATHSEP=='\\'
+ /* Put quotes around the string if there are spaces in it */
+ if (Ret.find_first_of(' ')!=string::npos && Ret[0]!='"')
+ {
+ Ret=g_QuoteString+Ret+g_QuoteString;
+ }
+#else
+ int Pos=0;
+ /* Escape the spaces with a backslash */
+ while ((Pos=Ret.find_first_of(' ',Pos))!=string::npos)
+ {
+ Ret=Ret.replace(Pos,1,"\\ ");
+ Pos+=2;
+ }
+#endif
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string UnquoteFileName(const string &Filename)
+{
+ size_t Pos=0;
+ string Name(Filename);
+#if OSPATHSEP=='\\'
+ /* Remove all the quotes from the filename */
+ while ((Pos=Name.find_first_of('"',Pos))!=string::npos)
+ {
+ Name=Name.replace(Pos,1,"");
+ }
+#else
+ /* Remove the escaped spaces */
+ while ((Pos=Name.find_first_of("\\",Pos))!=string::npos)
+ {
+ if (Name[Pos+1]==' ')
+ Name=Name.replace(Pos,2," ");
+ Pos+=1;
+ }
+#endif
+ return Name;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+fileinfo* fileinfo::GetDir() const
+{
+ return GetAbsFileInfo(m_AbsFileName.substr(0,m_AbsFileName.find_last_of(OSPATHSEP)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string fileinfo::GetName() const
+{
+ return m_AbsFileName.substr(m_AbsFileName.find_last_of(OSPATHSEP)+1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+mh_time_t fileinfo::realGetDate() const
+{
+ struct stat Buf;
+ if (-1==stat(m_AbsFileName.c_str(),&Buf))
+ ((fileinfo*)this)->m_Date.SetNotExist();
+ else if (S_ISDIR(Buf.st_mode))
+ ((fileinfo*)this)->m_Date.SetDir();
+ else
+ ((fileinfo*)this)->m_Date=Buf.st_mtime;
+ return m_Date;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool fileinfo::IsDir() const
+{
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(m_AbsFileName.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+ else
+ {
+ FindClose(hFind);
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+#else
+ struct stat Buf;
+ if (-1==stat(m_AbsFileName.c_str(),&Buf))
+ return false; // File does not exist, so consider this as not a directory
+ else
+ return 0!=S_ISDIR (Buf.st_mode);
+ return true;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void fileinfo::SetDateToNow()
+{
+ m_Date=time(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string fileinfo::GetPrerequisits() const
+{
+ // Build a string with all prerequisits, but make sure that every dependency
+ // is only in there once (we do this be building a set in parallel
+ vector<fileinfo*>::const_iterator DepIt=m_Deps.begin();
+ deps_t Deps;
+ bool first=true;
+ string Ret=g_EmptyString;
+ while (DepIt!=m_Deps.end())
+ {
+ deps_t::iterator pFound=Deps.find(*DepIt);
+ if (pFound==Deps.end())
+ {
+ if (first)
+ {
+ first=false;
+ }
+ else
+ {
+ Ret+=g_SpaceString;
+ }
+ Ret+=(*DepIt)->GetQuotedFullFileName();
+ }
+ Deps.insert(*DepIt);
+ DepIt++;
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void fileinfo::AddDeps(vector<fileinfo*> &Deps)
+{
+ vector<fileinfo*>::iterator It=Deps.begin();
+ vector<fileinfo*>::iterator ItEnd=Deps.end();
+ while (It!=ItEnd)
+ {
+ AddDep(*It++);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool fileinfo::IsAutoDepExtention(void) const
+{
+ const char *pName=GetFullFileName().c_str();
+ const char *pExt=strrchr(pName,'.');
+ if (!pExt)
+ return false;
+ pExt++;
+ if (m_pRule)
+ {
+ string ObjExt=m_pRule->GetMakefile()->ExpandVar(OBJEXTVAR);
+ return ((0==strcmp(pExt,ObjExt.c_str()+1)) || (0==strcmp(pExt,"h")));
+ }
+ else
+ return ((0==strcmp(pExt,"obj")) || (0==strcmp(pExt,"doj")) || (0==strcmp(pExt,"o")) || (0==strcmp(pExt,"h")));
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+string fileinfo::GetErrorMessageDuplicateRule(const refptr<rule>&pRule)
+{
+ string Ret;
+ Ret = GetQuotedFullFileName() + ": rule is defined multiple times\n";
+ Ret += "First (" + m_pRule->GetMakefile()->GetMakeDir()->GetQuotedFullFileName() + ") :\n";
+
+ vector<string>::const_iterator It=m_pRule->GetCommands().begin();
+ while (It!=m_pRule->GetCommands().end())
+ {
+ Ret+= " " + m_pRule->GetMakefile()->ExpandExpression(*It) + "\n";
+ It++;
+ }
+ Ret += "Second (" + pRule->GetMakefile()->GetMakeDir()->GetQuotedFullFileName() + ") :\n";
+ It=pRule->GetCommands().begin();
+ while (It!=pRule->GetCommands().end())
+ {
+ Ret += " " + pRule->GetMakefile()->ExpandExpression(*It) +"\n";
+ It++;
+ }
+ return Ret;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+string &NormalizePathName(string &Name)
+{
+ const char *pPtr=Name.c_str();
+ const char *pBeg=pPtr;
+ const char *pLastSlash=NULL;
+ char *pWr=(char*)pBeg;
+ char Char=*pPtr++;
+
+ while (Char)
+ {
+ if (Char=='\\' || Char=='/')
+ {
+ char Char2=pPtr[0];
+ if (Char2=='.')
+ {
+ if (pPtr[1]=='.')
+ {
+ pPtr+=2;
+ while ((pPtr[0]=='\\' || pPtr[0]=='/') && pPtr[1]=='.' && pPtr[2]=='.')
+ {
+ pLastSlash--;
+ while (*pLastSlash!='\\' && *pLastSlash!='/') pLastSlash--;
+ if (pLastSlash<pBeg)
+ pLastSlash=pBeg; // This is a fault in the file name, just put it back at the beginning
+ pPtr+=3;
+ }
+ if (pLastSlash)
+ pWr=(char*)pLastSlash;
+ }
+ else
+ {
+ if (pPtr[1]=='\\' || pPtr[1]=='/')
+ {
+ pPtr++;
+ }
+ else
+ {
+ pLastSlash=pWr;
+ *pWr++ = OSPATHSEP;
+ }
+ }
+ }
+ else if (Char2=='\\' || Char2=='/')
+ {
+ }
+ else
+ {
+ pLastSlash=pWr;
+ *pWr++ = OSPATHSEP;
+ }
+ }
+ else
+ {
+ #ifdef WIN32
+ *pWr++ = tolower(Char);
+ #else
+ *pWr++ = Char;
+ #endif
+ }
+ Char=*pPtr++;
+ }
+ *pWr=0;
+ Name.resize(pWr-pBeg);
+
+ return Name;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+fileinfo* GetFileInfo(const string &NameIn,const fileinfo* pRelDir)
+{
+ string Name=UnquoteFileName(NameIn);
+ bool DoesExist=true;
+
+ //Only concatenate if szName is not already a full name
+#ifdef WIN32
+ if (!Name.empty() && (Name.length()==1 || Name[1]!=':'))
+#endif
+ {
+ if (Name[0]!=OSPATHSEP)
+ {
+ Name=pRelDir->GetFullFileName()+OSPATHSEPSTR+Name;
+ if (!pRelDir->Exists()) /* if the directory does not exist, the file will not exist either */
+ DoesExist=false;
+ }
+ #ifdef WIN32
+ else
+ {
+ /* The filename is absolute but does not contain a driver letter. So add it (only on windows) */
+ Name=pRelDir->GetFullFileName().substr(0,2)+Name;
+ }
+ #endif
+ }
+ fileinfo* pRet=GetAbsFileInfo(NormalizePathName(Name));
+ if (!DoesExist)
+ pRet->SetNotExist();
+ return pRet;
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void PrintFileInfos()
+{
+ fileinfos::iterator pIt=g_FileInfos.begin();
+ while (pIt!=g_FileInfos.end())
+ {
+ cout<<(*pIt)->GetQuotedFullFileName()<<" :";
+ if ((*pIt)->IsPhony())
+ cout<<" (phony)";
+ vector<fileinfo*> &Deps=(*pIt)->GetDeps();
+ vector<fileinfo*>::iterator pDepIt=Deps.begin();
+ while (pDepIt!=Deps.end())
+ {
+ cout<<g_SpaceString<<(*pDepIt)->GetQuotedFullFileName();
+ pDepIt++;
+ }
+ cout<<endl;
+ // Write the commands
+ refptr<rule> pRule=(*pIt)->GetRule();
+ if (pRule)
+ {
+ cout<<g_SpaceString<<"Run in: "<<pRule->GetMakefile()->GetMakeDir()->GetQuotedFullFileName()<<endl;
+ pRule->PrintCommands();
+ }
+ pIt++;
+ }
+
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+fileinfos::~fileinfos()
+{
+ fileinfos::iterator It=begin();
+ while (It!=end())
+ {
+ delete (*It);
+ It++;
+ }
+}
diff --git a/tools/mhmake/src/fileinfo.h b/tools/mhmake/src/fileinfo.h
new file mode 100644
index 000000000..1d70f0df1
--- /dev/null
+++ b/tools/mhmake/src/fileinfo.h
@@ -0,0 +1,437 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __FILEINFO_H
+#define __FILEINFO_H
+
+#include "curdir.h"
+#include "rule.h"
+#include "md5.h"
+
+#ifdef WIN32
+#define OSPATHSEP '\\'
+#define OSPATHSEPSTR "\\"
+#define OSPATHENVSEP ';'
+#define OSPATHENVSEPSTR ";"
+#else
+#define OSPATHSEP '/'
+#define OSPATHSEPSTR "/"
+#define OSPATHENVSEP ':'
+#define OSPATHENVSEPSTR ":"
+#endif
+
+extern bool g_DumpOnError;
+extern bool g_PrintVarsAndRules;
+extern bool g_DoNotExecute;
+extern bool g_GenProjectTree;
+extern bool g_Quiet;
+extern bool g_RebuildAll;
+extern bool g_PrintAdditionalInfo;
+extern bool g_pPrintDependencyCheck;
+extern bool g_CheckCircularDeps;
+extern bool g_ForceAutoDepRescan;
+extern bool g_PrintLexYacc;
+extern bool g_Clean;
+extern bool g_StopCompiling;
+extern bool g_PrintMultipleDefinedRules;
+
+extern const string g_EmptyString;
+extern const string g_SpaceString;
+extern const string g_QuoteString;
+
+string QuoteFileName(const string &Filename);
+string UnquoteFileName(const string &Filename);
+
+template<typename T>
+inline string stringify(const T& x)
+{
+ ostringstream o;
+ o << x;
+ return o.str();
+}
+
+#define TIMESAFETY 3
+class mh_time
+{
+ enum
+ {
+ DATENOTVALID=0,
+ NOTEXISTTIME=1,
+ DIRTIME =2+TIMESAFETY
+ };
+ unsigned long m_Time;
+
+ bool operator < (const mh_time &Src);
+public:
+ mh_time(){m_Time=DATENOTVALID;}
+ mh_time(time_t Time) : m_Time((unsigned long)Time) {}
+ mh_time(unsigned long Time) : m_Time(Time) {}
+ mh_time(const mh_time &Time) : m_Time(Time.m_Time) {}
+
+ void SetDir(void)
+ {
+ m_Time=DIRTIME;
+ }
+ bool IsDir(void) const
+ {
+ return m_Time==DIRTIME;
+ }
+ void SetNotExist(void)
+ {
+ m_Time=NOTEXISTTIME;
+ }
+ bool IsExistingFile(void) const
+ {
+ return m_Time>DIRTIME;
+ }
+ bool DoesExist(void) const
+ {
+ return m_Time!=NOTEXISTTIME;
+ }
+ void Invalidate(void)
+ {
+ m_Time=DATENOTVALID;
+ }
+ bool IsDateValid(void) const
+ {
+ return m_Time!=DATENOTVALID;
+ }
+ friend ostream& operator<<(ostream& out,const mh_time &Src);
+
+ mh_time &operator = (const mh_time &Src) { m_Time=Src.m_Time; return *this;}
+
+ bool IsOlder(const mh_time &Src) const { return m_Time<Src.m_Time-TIMESAFETY; }
+
+ bool IsNewer(const mh_time &Src) const { return m_Time>Src.m_Time+TIMESAFETY; }
+ bool IsNewerOrSame(const mh_time &Src) const { return m_Time>=Src.m_Time-TIMESAFETY; }
+};
+typedef mh_time mh_time_t;
+
+inline ostream& operator<<(ostream& out,const mh_time &Src)
+{
+ out << hex << (unsigned long)Src.m_Time;
+ return out;
+}
+
+class fileinfo
+{
+ string m_AbsFileName;
+ bool m_IsPhony;
+ int m_BuildStatus; /* Bit 0 means the target built is started, Bit 1 means the target is still building */
+ refptr<rule> m_pRule;
+ mhmakefileparser *m_pAutoDepsMakefile;
+ vector<fileinfo*> m_Deps;
+ mh_time_t m_Date;
+ uint32 m_CommandsMd5_32; // 32-bit Md5 checksum of commands to build this target
+
+ fileinfo(const fileinfo &Src);
+ fileinfo(void);
+
+ void init(const string &AbsFileName,uint32 Md5_32)
+ {
+ m_IsPhony=false;
+ m_pAutoDepsMakefile=NULL;
+ m_BuildStatus=0;
+ m_AbsFileName=UnquoteFileName(AbsFileName);
+ InvalidateDate();
+ m_CommandsMd5_32=Md5_32;
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Initialising Md5 of "<<GetQuotedFullFileName().c_str()<<" to 0x"<<hex<<Md5_32<<endl;
+ #endif
+ }
+ fileinfo &operator = (const fileinfo &Src); // Do not allow copying
+public:
+ fileinfo(const string &AbsFileName,uint32 Md5_32)
+ {
+ init(AbsFileName,Md5_32);
+ }
+ fileinfo(const string &AbsFileName)
+ {
+ init(AbsFileName,0);
+ }
+ /* The following constructor is only used for name comparisons, and should only be used for that */
+ fileinfo(int Dummy,const string &AbsFileName)
+ {
+ m_AbsFileName=UnquoteFileName(AbsFileName);
+ }
+
+ fileinfo(const char *szFile)
+ {
+ init(szFile,0);
+ }
+ fileinfo(const char *szFile,uint32 Md5_32)
+ {
+ init(szFile,Md5_32);
+ }
+ const string &GetFullFileName(void) const
+ {
+ return m_AbsFileName;
+ }
+ string GetQuotedFullFileName(void) const
+ {
+ return QuoteFileName(m_AbsFileName);
+ }
+ void SetFullFileName(const string &strAbsName)
+ {
+ m_AbsFileName=UnquoteFileName(strAbsName);
+ // If the last char is path sep strip it
+ if (!m_AbsFileName.empty() && m_AbsFileName[m_AbsFileName.length()-1]==OSPATHSEP)
+ m_AbsFileName.resize(m_AbsFileName.length()-1);
+ }
+
+ fileinfo* GetDir(void) const;
+ string GetName() const;
+ bool IsDir() const;
+
+ string GetErrorMessageDuplicateRule(const refptr<rule> &pRule);
+
+ void SetRule(refptr<rule> &pRule)
+ {
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ if (m_pRule && m_pRule->GetCommands().size() && !IsBuilding()) {
+ DebugBreak();
+ }
+ #endif
+ m_pRule=pRule;
+ }
+
+ void SetRuleIfNotExist(refptr<rule> &pRule)
+ {
+ if (pRule)
+ {
+ if (!m_pRule)
+ {
+ SetRule(pRule);
+ pRule->AddTarget(this);
+ }
+ #ifdef _DEBUG
+ else
+ {
+ if (*m_pRule!=*pRule)
+ {
+ throw(GetErrorMessageDuplicateRule(pRule));
+ }
+ else if (g_PrintMultipleDefinedRules)
+ {
+ cerr<<GetErrorMessageDuplicateRule(pRule);
+ }
+ }
+ #endif
+ }
+ }
+
+ refptr<rule> GetRule(void) const
+ {
+ return m_pRule;
+ }
+ void AddDep(fileinfo *pDep)
+ {
+ if (&*pDep==this)
+ {
+ #ifdef _DEBUG
+ cout << GetQuotedFullFileName()<<" is directly dependent on itself\n";
+ #endif
+ return;
+ }
+ m_Deps.push_back(pDep);
+ }
+ void AddDeps(vector<fileinfo*> &Deps);
+
+ void InsertDeps(vector<fileinfo*> &Deps)
+ {
+ #ifdef _DEBUG
+ vector<fileinfo*> NewDeps;
+ vector<fileinfo*>::const_iterator It=Deps.begin();
+ vector<fileinfo*>::const_iterator ItEnd=Deps.end();
+ while (It!=ItEnd)
+ {
+ if (&**It==this)
+ {
+ #ifdef _DEBUG
+ cout << GetQuotedFullFileName()<<" is directly dependent on itself\n";
+ #endif
+ }
+ else
+ NewDeps.push_back(*It);
+ It++;
+ }
+ if (NewDeps.size())
+ m_Deps.insert(m_Deps.begin(),NewDeps.begin(),NewDeps.end());
+ #else
+ m_Deps.insert(m_Deps.begin(),Deps.begin(),Deps.end());
+ #endif
+ }
+ vector<fileinfo*> &GetDeps(void)
+ {
+ return m_Deps;
+ }
+ string GetPrerequisits(void) const;
+ void SetAutoDepsScan(mhmakefileparser *pMakefile)
+ {
+ #ifdef _DEBUG
+ if (m_pAutoDepsMakefile)
+ throw(string(".AUTODEPS can only defined ones for rule ")+GetFullFileName());
+ #endif
+ m_pAutoDepsMakefile=pMakefile;
+ }
+ mhmakefileparser *GetAutoDepsMakefile(void) const
+ {
+ return m_pAutoDepsMakefile;
+ }
+ void SetPhony(void)
+ {
+ m_IsPhony=true;
+ m_Date.SetNotExist(); // This will sure that this target will always be build (even if a corresponding file exists)
+ }
+ bool IsPhony(void) const
+ {
+ return m_IsPhony;
+ }
+ mh_time_t realGetDate(void) const;
+ void SetDateToNow(void);
+
+ void SetDate(mh_time_t Date)
+ {
+ m_Date=Date;
+ }
+
+ bool IsDateValid() const
+ {
+ return m_Date.IsDateValid();
+ }
+ void InvalidateDate(void)
+ {
+ m_Date.Invalidate();
+ }
+
+ mh_time_t GetDate(void) const
+ {
+ if (m_Date.IsDateValid())
+ return m_Date;
+ else
+ return realGetDate();
+ }
+ void SetNotExist(void)
+ { // this is used to make sure that this item is rebuild, even if it really exists
+ m_Date.SetNotExist();
+ }
+ bool Exists(void) const
+ {
+ return GetDate().DoesExist();
+ }
+ bool IsBuildStarted(void) const
+ {
+ return (m_BuildStatus&1)==1;
+ }
+ bool IsBuild(void) const
+ {
+ return m_BuildStatus==1;
+ }
+ void SetBuild(void)
+ {
+ m_BuildStatus=1;
+ }
+ bool IsBuilding(void) const
+ {
+ return (m_BuildStatus&2)==2;
+ }
+ void SetBuilding(bool Others=true)
+ {
+ m_BuildStatus|=2;
+ /* Check if there are targets build by the rule attached to this target, if so set them also to building */
+ if (Others && m_pRule)
+ {
+ m_pRule->SetTargetsIsBuilding(this);
+ }
+ }
+ void ClearBuilding(void)
+ {
+ m_BuildStatus&=~2;
+ }
+ bool IsAutoDepExtention(void) const;
+
+ void SetCommandsMd5_32(uint32 Md5_32)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Setting Md5 of "<<GetQuotedFullFileName()<<" to 0x"<<hex<<Md5_32<<endl;
+ #endif
+ m_CommandsMd5_32=Md5_32;
+ }
+#ifdef _DEBUG
+ uint32 GetCommandsMd5_32(void) const
+ {
+ return m_CommandsMd5_32;
+ }
+#endif
+ bool CompareMd5_32(uint32 Md5_32) const
+ {
+ return m_CommandsMd5_32==Md5_32;
+ }
+ void WriteMd5_32(FILE *pFile) const
+ {
+ fwrite(&m_CommandsMd5_32,sizeof(m_CommandsMd5_32),1,pFile);
+ }
+};
+
+struct less_fileinfo : public binary_function <const fileinfo*, const fileinfo*, bool>
+{
+ bool operator()(const fileinfo *_Left, const fileinfo *_Right) const
+ {
+ return less<string>().operator ()(_Left->GetFullFileName(),_Right->GetFullFileName());
+ }
+};
+
+fileinfo *GetFileInfo(const string &szName,const fileinfo* pRelDir);
+
+class fileinfos : public set<fileinfo*,less_fileinfo>
+{
+ public:
+ ~fileinfos();
+};
+
+extern fileinfos g_FileInfos;
+
+inline fileinfo *GetAbsFileInfo(const string &strAbsName)
+{
+ static fileinfo SearchFileInfo("");
+ SearchFileInfo.SetFullFileName(strAbsName);
+ /* Using find is just an optimalisation, you could use insert immediately */
+ fileinfos::const_iterator pFind=g_FileInfos.find(&SearchFileInfo);
+ if (pFind==g_FileInfos.end())
+ {
+ pair <fileinfos::iterator, bool> pPair=g_FileInfos.insert(new fileinfo(SearchFileInfo.GetFullFileName()));
+ return *(pPair.first);
+ }
+ else
+ return *pFind;
+}
+
+inline fileinfo *GetFileInfo(const char *szName,const fileinfo* pRelDir)
+{
+ return GetFileInfo(string(szName),pRelDir);
+}
+
+string &NormalizePathName(string &Name);
+void PrintFileInfos();
+
+#endif
+
diff --git a/tools/mhmake/src/flex.skl b/tools/mhmake/src/flex.skl
new file mode 100644
index 000000000..4d57bac46
--- /dev/null
+++ b/tools/mhmake/src/flex.skl
@@ -0,0 +1,3426 @@
+%# -*-C-*- vi: set ft=c:
+%# This file is processed in several stages.
+%# Here are the stages, as best as I can describe:
+%#
+%# 1. flex.skl is processed through GNU m4 during the
+%# pre-compilation stage of flex. Only macros starting
+%# with `m4preproc_' are processed, and quoting is normal.
+%#
+%# 2. The preprocessed skeleton is translated verbatim into a
+%# C array, saved as "skel.c" and compiled into the flex binary.
+%#
+%# 3. At runtime, the skeleton is generated and filtered (again)
+%# through m4. Macros beginning with `m4_' will be processed.
+%# The quoting is "[[" and "]]" so we don't interfere with
+%# user code.
+%#
+%# All generate macros for the m4 stage contain the text "m4" or "M4"
+%# in them. This is to distinguish them from CPP macros.
+%# The exception to this rule is YY_G, which is an m4 macro,
+%# but it needs to be remain short because it is used everywhere.
+%#
+/* A lexical scanner generated by flex */
+
+%# Macros for preproc stage.
+
+%# Macros for runtime processing stage.
+m4_changecom
+m4_changequote
+m4_changequote([[, ]])
+
+%#
+%# Lines in this skeleton starting with a "%" character are "control lines"
+%# and affect the generation of the scanner. The possible control codes are
+%# listed and processed in misc.c.
+%#
+%# %# - A comment. The current line is omitted from the generated scanner.
+%# %if-c++-only - The following lines are printed for C++ scanners ONLY.
+%# %if-c-only - The following lines are NOT printed for C++ scanners.
+%# %if-c-or-c++ - The following lines are printed in BOTH C and C++ scanners.
+%# %if-reentrant - Print for reentrant scanners.(push)
+%# %if-not-reentrant - Print for non-reentrant scanners. (push)
+%# %if-bison-bridge - Print for bison-bridge. (push)
+%# %if-not-bison-bridge - Print for non-bison-bridge. (push)
+%# %endif - pop from the previous if code.
+%# %% - A stop-point, where code is inserted by flex.
+%# Each stop-point is numbered here and also in the code generator.
+%# (See gen.c, etc. for details.)
+%# %not-for-header - Begin code that should NOT appear in a ".h" file.
+%# %ok-for-header - %c and %e are used for building a header file.
+%# %if-tables-serialization
+%#
+%# All control-lines EXCEPT comment lines ("%#") will be inserted into
+%# the generated scanner as a C-style comment. This is to aid those who
+%# edit the skeleton.
+%#
+
+%not-for-header
+%if-c-only
+%if-not-reentrant
+m4_ifelse(M4_YY_PREFIX,yy,,
+#define yy_create_buffer M4_YY_PREFIX[[_create_buffer]]
+#define yy_delete_buffer M4_YY_PREFIX[[_delete_buffer]]
+#define yy_flex_debug M4_YY_PREFIX[[_flex_debug]]
+#define yy_init_buffer M4_YY_PREFIX[[_init_buffer]]
+#define yy_flush_buffer M4_YY_PREFIX[[_flush_buffer]]
+#define yy_load_buffer_state M4_YY_PREFIX[[_load_buffer_state]]
+#define yy_switch_to_buffer M4_YY_PREFIX[[_switch_to_buffer]]
+#define yyin M4_YY_PREFIX[[in]]
+#define yyleng M4_YY_PREFIX[[leng]]
+#define yylex M4_YY_PREFIX[[lex]]
+#define yylineno M4_YY_PREFIX[[lineno]]
+#define yyout M4_YY_PREFIX[[out]]
+#define yyrestart M4_YY_PREFIX[[restart]]
+#define yytext M4_YY_PREFIX[[text]]
+#define yywrap M4_YY_PREFIX[[wrap]]
+#define yyalloc M4_YY_PREFIX[[alloc]]
+#define yyrealloc M4_YY_PREFIX[[realloc]]
+#define yyfree M4_YY_PREFIX[[free]]
+)
+%endif
+%endif
+%ok-for-header
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION FLEX_MAJOR_VERSION
+#define YY_FLEX_MINOR_VERSION FLEX_MINOR_VERSION
+#define YY_FLEX_SUBMINOR_VERSION FLEX_SUBMINOR_VERSION
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+%# Some negated symbols
+m4_ifdef( [[M4_YY_IN_HEADER]], , [[m4_define([[M4_YY_NOT_IN_HEADER]], [[]])]])
+m4_ifdef( [[M4_YY_REENTRANT]], , [[m4_define([[M4_YY_NOT_REENTRANT]], [[]])]])
+
+%# This is the m4 way to say "(stack_used || is_reentrant)
+m4_ifdef( [[M4_YY_STACK_USED]], [[m4_define([[M4_YY_HAS_START_STACK_VARS]])]])
+m4_ifdef( [[M4_YY_REENTRANT]], [[m4_define([[M4_YY_HAS_START_STACK_VARS]])]])
+
+%# Prefixes.
+%# The complexity here is necessary so that m4 preserves
+%# the argument lists to each C function.
+
+
+m4_ifdef( [[M4_YY_PREFIX]],, [[m4_define([[M4_YY_PREFIX]], [[yy]])]])
+
+%if-c++-only
+ /* The c++ scanner is a mess. The FlexLexer.h header file relies on the
+ * following macro. This is required in order to pass the c++-multiple-scanners
+ * test in the regression suite. We get reports that it breaks inheritance.
+ * We will address this in a future release of flex, or omit the C++ scanner
+ * altogether.
+ */
+ #define yyFlexLexer M4_YY_PREFIX[[FlexLexer]]
+%endif
+
+%if-c-only
+ M4_GEN_PREFIX(`_create_buffer')
+ M4_GEN_PREFIX(`_delete_buffer')
+ M4_GEN_PREFIX(`_scan_buffer')
+ M4_GEN_PREFIX(`_scan_string')
+ M4_GEN_PREFIX(`_scan_bytes')
+ M4_GEN_PREFIX(`_init_buffer')
+ M4_GEN_PREFIX(`_flush_buffer')
+ M4_GEN_PREFIX(`_load_buffer_state')
+ M4_GEN_PREFIX(`_switch_to_buffer')
+ M4_GEN_PREFIX(`push_buffer_state')
+ M4_GEN_PREFIX(`pop_buffer_state')
+ M4_GEN_PREFIX(`ensure_buffer_stack')
+ M4_GEN_PREFIX(`lex')
+ M4_GEN_PREFIX(`restart')
+ M4_GEN_PREFIX(`lex_init')
+ M4_GEN_PREFIX(`lex_init_extra')
+ M4_GEN_PREFIX(`lex_destroy')
+ M4_GEN_PREFIX(`get_debug')
+ M4_GEN_PREFIX(`set_debug')
+ M4_GEN_PREFIX(`get_extra')
+ M4_GEN_PREFIX(`set_extra')
+ M4_GEN_PREFIX(`get_in')
+ M4_GEN_PREFIX(`set_in')
+ M4_GEN_PREFIX(`get_out')
+ M4_GEN_PREFIX(`set_out')
+ M4_GEN_PREFIX(`get_leng')
+ M4_GEN_PREFIX(`get_text')
+ M4_GEN_PREFIX(`get_lineno')
+ M4_GEN_PREFIX(`set_lineno')
+ m4_ifdef( [[M4_YY_REENTRANT]],
+ [[
+ M4_GEN_PREFIX(`get_column')
+ M4_GEN_PREFIX(`set_column')
+ ]])
+ M4_GEN_PREFIX(`wrap')
+%endif
+
+m4_ifdef( [[M4_YY_BISON_LVAL]],
+[[
+ M4_GEN_PREFIX(`get_lval')
+ M4_GEN_PREFIX(`set_lval')
+]])
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ M4_GEN_PREFIX(`get_lloc')
+ M4_GEN_PREFIX(`set_lloc')
+]])
+
+%if-c-only
+m4_ifdef( [[M4_YY_NOT_REENTRANT]],
+[[
+ M4_GEN_PREFIX(`text')
+ M4_GEN_PREFIX(`leng')
+ M4_GEN_PREFIX(`in')
+ M4_GEN_PREFIX(`out')
+ M4_GEN_PREFIX(`_flex_debug')
+ M4_GEN_PREFIX(`lineno')
+]])
+%endif
+
+
+m4_ifdef( [[M4_YY_TABLES_EXTERNAL]],
+[[
+ M4_GEN_PREFIX(`tables_fload')
+ M4_GEN_PREFIX(`tables_destroy')
+ M4_GEN_PREFIX(`TABLES_NAME')
+]])
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+%if-c-only
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+%endif
+
+%if-tables-serialization
+#include <sys/types.h>
+#include <netinet/in.h>
+%endif
+/* end standard C headers. */
+
+%if-c-or-c++
+#include <flexint.h>
+%endif
+
+%if-c++-only
+/* begin standard C++ headers. */
+#include <iostream>
+#include <errno.h>
+#include <cstdlib>
+#include <cstring>
+/* end standard C++ headers. */
+%endif
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+%# For compilers that can not handle prototypes.
+%# e.g.,
+%# The function prototype
+%# int foo(int x, char* y);
+%#
+%# ...should be written as
+%# int foo M4_YY_PARAMS(int x, char* y);
+%#
+%# ...which could possibly generate
+%# int foo ();
+%#
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_PROTOS]],
+[[
+ m4_define( [[M4_YY_PARAMS]], [[()]])
+]],
+[[
+ m4_define( [[M4_YY_PARAMS]], [[($*)]])
+]])
+
+%not-for-header
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+%ok-for-header
+
+%not-for-header
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+%ok-for-header
+
+
+
+%if-reentrant
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+%# Declare yyguts variable
+m4_define( [[M4_YY_DECL_GUTS_VAR]], [[struct yyguts_t * yyg = (struct yyguts_t*)yyscanner]])
+%# For use wherever a Global is accessed or assigned.
+m4_define( [[YY_G]], [[yyg->$1]])
+
+%# For use in function prototypes to append the additional argument.
+m4_define( [[M4_YY_PROTO_LAST_ARG]], [[, yyscan_t yyscanner]])
+m4_define( [[M4_YY_PROTO_ONLY_ARG]], [[yyscan_t yyscanner]])
+
+%# For use in function definitions to append the additional argument.
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_DEFS]],
+[[
+ m4_define( [[M4_YY_DEF_LAST_ARG]], [[, yyscanner]])
+ m4_define( [[M4_YY_DEF_ONLY_ARG]], [[yyscanner]])
+]],
+[[
+ m4_define( [[M4_YY_DEF_LAST_ARG]], [[, yyscan_t yyscanner]])
+ m4_define( [[M4_YY_DEF_ONLY_ARG]], [[yyscan_t yyscanner]])
+]])
+m4_define( [[M4_YY_DECL_LAST_ARG]], [[yyscan_t yyscanner;]])
+
+%# For use in function calls to pass the additional argument.
+m4_define( [[M4_YY_CALL_LAST_ARG]], [[, yyscanner]])
+m4_define( [[M4_YY_CALL_ONLY_ARG]], [[yyscanner]])
+
+%# For use in function documentation to adjust for additional argument.
+m4_define( [[M4_YY_DOC_PARAM]], [[@param yyscanner The scanner object.]])
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin YY_G(yyin_r)
+#define yyout YY_G(yyout_r)
+#define yyextra YY_G(yyextra_r)
+#define yyleng YY_G(yyleng_r)
+#define yytext YY_G(yytext_r)
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug YY_G(yy_flex_debug_r)
+
+m4_define( [[M4_YY_INCR_LINENO]],
+[[
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+]])
+
+%endif
+
+
+
+%if-not-reentrant
+
+m4_define( [[M4_YY_INCR_LINENO]],
+[[
+ yylineno++;
+]])
+
+%# Define these macros to be no-ops.
+m4_define( [[M4_YY_DECL_GUTS_VAR]], [[m4_dnl]])
+m4_define( [[YY_G]], [[($1)]])
+m4_define( [[M4_YY_PROTO_LAST_ARG]])
+m4_define( [[M4_YY_PROTO_ONLY_ARG]], [[void]])
+m4_define( [[M4_YY_DEF_LAST_ARG]])
+
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_DEFS]],
+[[
+ m4_define( [[M4_YY_DEF_ONLY_ARG]])
+]],
+[[
+ m4_define( [[M4_YY_DEF_ONLY_ARG]], [[void]])
+]])
+m4_define([[M4_YY_DECL_LAST_ARG]])
+m4_define([[M4_YY_CALL_LAST_ARG]])
+m4_define([[M4_YY_CALL_ONLY_ARG]])
+m4_define( [[M4_YY_DOC_PARAM]], [[]])
+
+%endif
+
+
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_DEFS]],
+[[
+%# For compilers that need traditional function definitions.
+%# e.g.,
+%# The function prototype taking 2 arguments
+%# int foo (int x, char* y)
+%#
+%# ...should be written as
+%# int foo YYFARGS2(int,x, char*,y)
+%#
+%# ...which could possibly generate
+%# int foo (x,y,yyscanner)
+%# int x;
+%# char * y;
+%# yyscan_t yyscanner;
+%#
+%# Generate traditional function defs
+ m4_define( [[YYFARGS0]], [[(M4_YY_DEF_ONLY_ARG) [[\]]
+ M4_YY_DECL_LAST_ARG]])
+ m4_define( [[YYFARGS1]], [[($2 M4_YY_DEF_LAST_ARG) [[\]]
+ $1 $2; [[\]]
+ M4_YY_DECL_LAST_ARG]])
+ m4_define( [[YYFARGS2]], [[($2,$4 M4_YY_DEF_LAST_ARG) [[\]]
+ $1 $2; [[\]]
+ $3 $4; [[\]]
+ M4_YY_DECL_LAST_ARG]])
+ m4_define( [[YYFARGS3]], [[($2,$4,$6 M4_YY_DEF_LAST_ARG) [[\]]
+ $1 $2; [[\]]
+ $3 $4; [[\]]
+ $5 $6; [[\]]
+ M4_YY_DECL_LAST_ARG]])
+]],
+[[
+%# Generate C99 function defs.
+ m4_define( [[YYFARGS0]], [[(M4_YY_DEF_ONLY_ARG)]])
+ m4_define( [[YYFARGS1]], [[($1 $2 M4_YY_DEF_LAST_ARG)]])
+ m4_define( [[YYFARGS2]], [[($1 $2, $3 $4 M4_YY_DEF_LAST_ARG)]])
+ m4_define( [[YYFARGS3]], [[($1 $2, $3 $4, $5 $6 M4_YY_DEF_LAST_ARG)]])
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN YY_G(yy_start) = 1 + 2 *
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((YY_G(yy_start) - 1) / 2)
+#define YYSTATE YY_START
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin M4_YY_CALL_LAST_ARG )
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define YY_END_OF_BUFFER_CHAR 0
+]])
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+]])
+
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+%if-not-reentrant
+extern int yyleng;
+%endif
+
+%if-c-only
+%if-not-reentrant
+extern FILE *yyin, *yyout;
+%endif
+%endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+ m4_ifdef( [[M4_YY_USE_LINENO]],
+ [[
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < yyleng; ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+ ]],
+ [[
+ #define YY_LESS_LINENO(n)
+ ]])
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = YY_G(yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ YY_G(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define unput(c) yyunput( c, YY_G(yytext_ptr) M4_YY_CALL_LAST_ARG )
+]])
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+%if-c-only
+ FILE *yy_input_file;
+%endif
+
+%if-c++-only
+ std::istream* yy_input_file;
+%endif
+
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+]])
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+%if-c-only Standard (non-C++) definition
+%not-for-header
+%if-not-reentrant
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+%endif
+%ok-for-header
+%endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( YY_G(yy_buffer_stack) \
+ ? YY_G(yy_buffer_stack)[YY_G(yy_buffer_stack_top)] \
+ : NULL)
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE YY_G(yy_buffer_stack)[YY_G(yy_buffer_stack_top)]
+]])
+
+%if-c-only Standard (non-C++) definition
+
+%if-not-reentrant
+%not-for-header
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+%ok-for-header
+%endif
+
+void yyrestart M4_YY_PARAMS( FILE *input_file M4_YY_PROTO_LAST_ARG );
+void yy_switch_to_buffer M4_YY_PARAMS( YY_BUFFER_STATE new_buffer M4_YY_PROTO_LAST_ARG );
+YY_BUFFER_STATE yy_create_buffer M4_YY_PARAMS( FILE *file, int size M4_YY_PROTO_LAST_ARG );
+void yy_delete_buffer M4_YY_PARAMS( YY_BUFFER_STATE b M4_YY_PROTO_LAST_ARG );
+void yy_flush_buffer M4_YY_PARAMS( YY_BUFFER_STATE b M4_YY_PROTO_LAST_ARG );
+void yypush_buffer_state M4_YY_PARAMS( YY_BUFFER_STATE new_buffer M4_YY_PROTO_LAST_ARG );
+void yypop_buffer_state M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+static void yyensure_buffer_stack M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+static void yy_load_buffer_state M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+static void yy_init_buffer M4_YY_PARAMS( YY_BUFFER_STATE b, FILE *file M4_YY_PROTO_LAST_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER M4_YY_CALL_LAST_ARG)
+]])
+
+YY_BUFFER_STATE yy_scan_buffer M4_YY_PARAMS( char *base, yy_size_t size M4_YY_PROTO_LAST_ARG );
+YY_BUFFER_STATE yy_scan_string M4_YY_PARAMS( yyconst char *yy_str M4_YY_PROTO_LAST_ARG );
+YY_BUFFER_STATE yy_scan_bytes M4_YY_PARAMS( yyconst char *bytes, int len M4_YY_PROTO_LAST_ARG );
+
+%endif
+
+void *yyalloc M4_YY_PARAMS( yy_size_t M4_YY_PROTO_LAST_ARG );
+void *yyrealloc M4_YY_PARAMS( void *, yy_size_t M4_YY_PROTO_LAST_ARG );
+void yyfree M4_YY_PARAMS( void * M4_YY_PROTO_LAST_ARG );
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define yy_new_buffer yy_create_buffer
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (M4_YY_CALL_ONLY_ARG); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE M4_YY_CALL_LAST_ARG); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (M4_YY_CALL_ONLY_ARG); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE M4_YY_CALL_LAST_ARG); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+]])
+
+%% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here
+
+%if-c-only Standard (non-C++) definition
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+static yy_state_type yy_get_previous_state M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+static yy_state_type yy_try_NUL_trans M4_YY_PARAMS( yy_state_type current_state M4_YY_PROTO_LAST_ARG);
+static int yy_get_next_buffer M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+static void yy_fatal_error M4_YY_PARAMS( yyconst char msg[] M4_YY_PROTO_LAST_ARG );
+]])
+
+%endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ YY_G(yytext_ptr) = yy_bp; \
+%% [2.0] code to fiddle yytext and yyleng for yymore() goes here \
+ YY_G(yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+%% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \
+ YY_G(yy_c_buf_p) = yy_cp;
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+%% [4.0] data tables for the DFA and the user's section 1 definitions go here
+]])
+
+m4_ifdef( [[M4_YY_IN_HEADER]], [[#ifdef YY_HEADER_EXPORT_START_CONDITIONS]])
+M4_YY_SC_DEFS
+m4_ifdef( [[M4_YY_IN_HEADER]], [[#endif]])
+
+m4_ifdef( [[M4_YY_NO_UNISTD_H]],,
+[[
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+%if-c-only
+#include <unistd.h>
+%endif
+%if-c++-only
+#include <unistd.h>
+%endif
+#endif
+]])
+
+m4_ifdef( [[M4_EXTRA_TYPE_DEFS]],
+[[
+#define YY_EXTRA_TYPE M4_EXTRA_TYPE_DEFS
+]],
+[[
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+]]
+)
+
+%if-c-only Reentrant structure and macros (non-C++).
+%if-reentrant
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ yy_state_type *yy_state_buf;
+ yy_state_type *yy_state_ptr;
+ char *yy_full_match;
+ int yy_lp;
+
+ /* These are only needed for trailing context rules,
+ * but there's no conditional variable for that yet. */
+ int yy_looking_for_trail_begin;
+ int yy_full_lp;
+ int *yy_full_state;
+]])
+
+m4_ifdef( [[M4_YY_TEXT_IS_ARRAY]],
+[[
+ char yytext_r[YYLMAX];
+ char *yytext_ptr;
+ int yy_more_offset;
+ int yy_prev_more_offset;
+]],
+[[
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+]])
+
+m4_ifdef( [[M4_YY_BISON_LVAL]],
+[[
+ YYSTYPE * yylval_r;
+]])
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ YYLTYPE * yylloc_r;
+]])
+
+ }; /* end struct yyguts_t */
+]])
+
+
+%if-c-only
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+static int yy_init_globals M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+%endif
+
+%if-reentrant
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+ m4_ifdef( [[M4_YY_BISON_LVAL]],
+ [[
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval YY_G(yylval_r)
+ ]])
+
+ m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+ [[
+ # define yylloc YY_G(yylloc_r)
+ ]])
+]])
+
+int yylex_init M4_YY_PARAMS(yyscan_t* scanner);
+
+int yylex_init_extra M4_YY_PARAMS( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+%endif
+
+%endif End reentrant structures and macros.
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+m4_ifdef( [[M4_YY_NO_DESTROY]],,
+[[
+int yylex_destroy M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_DEBUG]],,
+[[
+int yyget_debug M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_DEBUG]],,
+[[
+void yyset_debug M4_YY_PARAMS( int debug_flag M4_YY_PROTO_LAST_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_EXTRA]],,
+[[
+YY_EXTRA_TYPE yyget_extra M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_EXTRA]],,
+[[
+void yyset_extra M4_YY_PARAMS( YY_EXTRA_TYPE user_defined M4_YY_PROTO_LAST_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_IN]],,
+[[
+FILE *yyget_in M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_IN]],,
+[[
+void yyset_in M4_YY_PARAMS( FILE * in_str M4_YY_PROTO_LAST_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_OUT]],,
+[[
+FILE *yyget_out M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_OUT]],,
+[[
+void yyset_out M4_YY_PARAMS( FILE * out_str M4_YY_PROTO_LAST_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_LENG]],,
+[[
+int yyget_leng M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_TEXT]],,
+[[
+char *yyget_text M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_LINENO]],,
+[[
+int yyget_lineno M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_LINENO]],,
+[[
+void yyset_lineno M4_YY_PARAMS( int line_number M4_YY_PROTO_LAST_ARG );
+]])
+
+%if-bison-bridge
+m4_ifdef( [[M4_YY_NO_GET_LVAL]],,
+[[
+YYSTYPE * yyget_lval M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+]])
+
+void yyset_lval M4_YY_PARAMS( YYSTYPE * yylval_param M4_YY_PROTO_LAST_ARG );
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ m4_ifdef( [[M4_YY_NO_GET_LLOC]],,
+ [[
+ YYLTYPE *yyget_lloc M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+ ]])
+
+ m4_ifdef( [[M4_YY_NO_SET_LLOC]],,
+ [[
+ void yyset_lloc M4_YY_PARAMS( YYLTYPE * yylloc_param M4_YY_PROTO_LAST_ARG );
+ ]])
+]])
+%endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+#else
+extern int yywrap M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+#endif
+#endif
+
+%not-for-header
+ m4_ifdef( [[M4_YY_NO_UNPUT]],,
+ [[
+ static void yyunput M4_YY_PARAMS( int c, char *buf_ptr M4_YY_PROTO_LAST_ARG);
+ ]])
+%ok-for-header
+%endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy M4_YY_PARAMS( char *, yyconst char *, int M4_YY_PROTO_LAST_ARG);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen M4_YY_PARAMS( yyconst char * M4_YY_PROTO_LAST_ARG);
+#endif
+
+#ifndef YY_NO_INPUT
+%if-c-only Standard (non-C++) definition
+%not-for-header
+#ifdef __cplusplus
+static int yyinput M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+#else
+static int input M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+#endif
+%ok-for-header
+%endif
+#endif
+
+
+%if-c-only
+%# TODO: This is messy.
+m4_ifdef( [[M4_YY_STACK_USED]],
+[[
+
+m4_ifdef( [[M4_YY_NOT_REENTRANT]],
+[[
+ m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+ [[
+ static int yy_start_stack_ptr = 0;
+ static int yy_start_stack_depth = 0;
+ static int *yy_start_stack = NULL;
+ ]])
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+ m4_ifdef( [[M4_YY_NO_PUSH_STATE]],,
+ [[
+ static void yy_push_state M4_YY_PARAMS( int new_state M4_YY_PROTO_LAST_ARG);
+ ]])
+ m4_ifdef( [[M4_YY_NO_POP_STATE]],,
+ [[
+ static void yy_pop_state M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+ ]])
+ m4_ifdef( [[M4_YY_NO_TOP_STATE]],,
+ [[
+ static int yy_top_state M4_YY_PARAMS( M4_YY_PROTO_ONLY_ARG );
+ ]])
+]])
+
+]],
+[[
+m4_define( [[M4_YY_NO_PUSH_STATE]])
+m4_define( [[M4_YY_NO_POP_STATE]])
+m4_define( [[M4_YY_NO_TOP_STATE]])
+]])
+%endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+%if-c-only Standard (non-C++) definition
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+%endif
+%if-c++-only C++ definition
+#define ECHO LexerOutput( yytext, yyleng )
+%endif
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+%% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \
+\
+%if-c++-only C++ definition \
+ if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+%endif
+
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+]])
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+%if-c-only
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg M4_YY_CALL_LAST_ARG)
+%endif
+%if-c++-only
+#define YY_FATAL_ERROR(msg) LexerError( msg )
+%endif
+#endif
+]])
+
+%if-tables-serialization structures and prototypes
+m4preproc_include(`tables_shared.h')
+
+/* Load the DFA tables from the given stream. */
+int yytables_fload M4_YY_PARAMS(FILE * fp M4_YY_PROTO_LAST_ARG);
+
+/* Unload the tables from memory. */
+int yytables_destroy M4_YY_PARAMS(M4_YY_PROTO_ONLY_ARG);
+%not-for-header
+
+/** Describes a mapping from a serialized table id to its deserialized state in
+ * this scanner. This is the bridge between our "generic" deserialization code
+ * and the specifics of this scanner.
+ */
+struct yytbl_dmap {
+ enum yytbl_id dm_id;/**< table identifier */
+ void **dm_arr; /**< address of pointer to store the deserialized table. */
+ size_t dm_sz; /**< local sizeof() each element in table. */
+};
+
+/** A {0,0,0}-terminated list of structs, forming the map */
+static struct yytbl_dmap yydmap[] =
+{
+%tables-yydmap generated elements
+ {0,0,0}
+};
+
+/** A tables-reader object to maintain some state in the read. */
+struct yytbl_reader {
+ FILE * fp; /**< input stream */
+ flex_uint32_t bread; /**< bytes read since beginning of current tableset */
+};
+
+%endif
+/* end tables serialization structures and prototypes */
+
+%ok-for-header
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+%if-c-only Standard (non-C++) definition
+
+
+m4_define( [[M4_YY_LEX_PROTO]], [[M4_YY_PARAMS(M4_YY_PROTO_ONLY_ARG)]])
+m4_define( [[M4_YY_LEX_DECLARATION]], [[YYFARGS0(void)]])
+
+m4_ifdef( [[M4_YY_BISON_LVAL]],
+[[
+ m4_dnl The bison pure parser is used. Redefine yylex to
+ m4_dnl accept the lval parameter.
+
+ m4_define( [[M4_YY_LEX_PROTO]], [[\]]
+ [[M4_YY_PARAMS(YYSTYPE * yylval_param M4_YY_PROTO_LAST_ARG)]])
+ m4_define( [[M4_YY_LEX_DECLARATION]], [[\]]
+ [[YYFARGS1(YYSTYPE *,yylval_param)]])
+]])
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ m4_dnl Locations are used. yylex should also accept the ylloc parameter.
+
+ m4_define( [[M4_YY_LEX_PROTO]], [[\]]
+ [[M4_YY_PARAMS(YYSTYPE * yylval_param, YYLTYPE * yylloc_param M4_YY_PROTO_LAST_ARG)]])
+ m4_define( [[M4_YY_LEX_DECLARATION]], [[\]]
+ [[YYFARGS2(YYSTYPE *,yylval_param, YYLTYPE *,yylloc_param)]])
+]])
+
+extern int yylex M4_YY_LEX_PROTO;
+
+#define YY_DECL int yylex M4_YY_LEX_DECLARATION
+%endif
+%if-c++-only C++ definition
+#define YY_DECL int yyFlexLexer::yylex()
+%endif
+#endif /* !YY_DECL */
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+%% [6.0] YY_RULE_SETUP definition goes here
+]])
+
+%not-for-header
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ M4_YY_DECL_GUTS_VAR();
+
+m4_ifdef( [[M4_YY_NOT_REENTRANT]],
+[[
+ m4_ifdef( [[M4_YY_BISON_LVAL]],
+ [[
+ YYSTYPE * yylval;
+ ]])
+ m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+ [[
+ YYLTYPE * yylloc;
+ ]])
+]])
+
+%% [7.0] user's declarations go here
+
+m4_ifdef( [[M4_YY_BISON_LVAL]],
+[[
+ yylval = yylval_param;
+]])
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ yylloc = yylloc_param;
+]])
+
+ if ( !YY_G(yy_init) )
+ {
+ YY_G(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ /* Create the reject buffer large enough to save one state per allowed character. */
+ if ( ! YY_G(yy_state_buf) )
+ YY_G(yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE M4_YY_CALL_LAST_ARG);
+ if ( ! YY_G(yy_state_buf) )
+ YY_FATAL_ERROR( "out of dynamic memory in yylex()" );
+]])
+
+ if ( ! YY_G(yy_start) )
+ YY_G(yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+%if-c-only
+ yyin = stdin;
+%endif
+%if-c++-only
+ yyin = & std::cin;
+%endif
+
+ if ( ! yyout )
+%if-c-only
+ yyout = stdout;
+%endif
+%if-c++-only
+ yyout = & std::cout;
+%endif
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (M4_YY_CALL_ONLY_ARG);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE M4_YY_CALL_LAST_ARG);
+ }
+
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+%% [8.0] yymore()-related code goes here
+ yy_cp = YY_G(yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = YY_G(yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+%% [9.0] code to set up and find next match goes here
+
+yy_find_action:
+%% [10.0] code to find the action number goes here
+
+ YY_DO_BEFORE_ACTION;
+
+%% [11.0] code for yylineno update goes here
+
+do_action: /* This label is used only to access EOF actions. */
+
+%% [12.0] debug code goes here
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+%% [13.0] actions go here
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - YY_G(yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = YY_G(yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ YY_G(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( YY_G(yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ YY_G(yy_c_buf_p) = YY_G(yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( M4_YY_CALL_ONLY_ARG );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state M4_YY_CALL_LAST_ARG);
+
+ yy_bp = YY_G(yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++YY_G(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+%% [14.0] code to do back-up for compressed tables and set up yy_cp goes here
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( M4_YY_CALL_ONLY_ARG ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ YY_G(yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( M4_YY_CALL_ONLY_ARG ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ YY_G(yy_c_buf_p) = YY_G(yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! YY_G(yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ YY_G(yy_c_buf_p) =
+ YY_G(yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( M4_YY_CALL_ONLY_ARG );
+
+ yy_cp = YY_G(yy_c_buf_p);
+ yy_bp = YY_G(yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ YY_G(yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( M4_YY_CALL_ONLY_ARG );
+
+ yy_cp = YY_G(yy_c_buf_p);
+ yy_bp = YY_G(yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+%ok-for-header
+
+%if-c++-only
+%not-for-header
+/* The contents of this function are C++ specific, so the YY_G macro is not used.
+ */
+yyFlexLexer::yyFlexLexer( std::istream* arg_yyin, std::ostream* arg_yyout )
+{
+ yyin = arg_yyin;
+ yyout = arg_yyout;
+ yy_c_buf_p = 0;
+ yy_init = 0;
+ yy_start = 0;
+ yy_flex_debug = 0;
+ yylineno = 1; // this will only get updated if %option yylineno
+
+ yy_did_buffer_switch_on_eof = 0;
+
+ yy_looking_for_trail_begin = 0;
+ yy_more_flag = 0;
+ yy_more_len = 0;
+ yy_more_offset = yy_prev_more_offset = 0;
+
+ yy_start_stack_ptr = yy_start_stack_depth = 0;
+ yy_start_stack = NULL;
+
+ yy_buffer_stack = 0;
+ yy_buffer_stack_top = 0;
+ yy_buffer_stack_max = 0;
+
+
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ yy_state_buf = new yy_state_type[YY_STATE_BUF_SIZE];
+]],
+[[
+ yy_state_buf = 0;
+]])
+}
+
+/* The contents of this function are C++ specific, so the YY_G macro is not used.
+ */
+yyFlexLexer::~yyFlexLexer()
+{
+ mystack::iterator It=m_IncludeStack.begin();
+ while (It!=m_IncludeStack.end())
+ {
+ yy_delete_buffer((*It)->m_BufferState); /* When the stack is deleted, the lexer is not run anymore, so we have to clean up the buffers ourself */
+ It++;
+ }
+ delete [] yy_state_buf;
+ yyfree( yy_start_stack M4_YY_CALL_LAST_ARG );
+ yy_delete_buffer( YY_CURRENT_BUFFER M4_YY_CALL_LAST_ARG);
+ yyfree( yy_buffer_stack M4_YY_CALL_LAST_ARG );
+}
+
+/* The contents of this function are C++ specific, so the YY_G macro is not used.
+ */
+void yyFlexLexer::switch_streams( std::istream* new_in, std::ostream* new_out )
+{
+ if ( new_in )
+ {
+ yy_delete_buffer( YY_CURRENT_BUFFER M4_YY_CALL_LAST_ARG);
+ yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE M4_YY_CALL_LAST_ARG) M4_YY_CALL_LAST_ARG);
+ }
+
+ if ( new_out )
+ yyout = new_out;
+}
+
+#ifdef YY_INTERACTIVE
+int yyFlexLexer::LexerInput( char* buf, int /* max_size */ )
+#else
+int yyFlexLexer::LexerInput( char* buf, int max_size )
+#endif
+{
+ if ( yyin->eof() || yyin->fail() )
+ return 0;
+
+#ifdef YY_INTERACTIVE
+ yyin->get( buf[0] );
+
+ if ( yyin->eof() )
+ return 0;
+
+ if ( yyin->bad() )
+ return -1;
+
+ return 1;
+
+#else
+ (void) yyin->read( buf, max_size );
+
+ if ( yyin->bad() )
+ return -1;
+ else
+ return (int)yyin->gcount();
+#endif
+}
+
+void yyFlexLexer::LexerOutput( const char* buf, int size )
+{
+ (void) yyout->write( buf, size );
+}
+%ok-for-header
+%endif
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+%if-c-only
+static int yy_get_next_buffer YYFARGS0(void)
+%endif
+%if-c++-only
+int yyFlexLexer::yy_get_next_buffer()
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = YY_G(yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( YY_G(yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( YY_G(yy_c_buf_p) - YY_G(yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (YY_G(yy_c_buf_p) - YY_G(yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = YY_G(yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+]],
+[[
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (YY_G(yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 M4_YY_CALL_LAST_ARG );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ YY_G(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+]])
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ YY_G(yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = YY_G(yy_n_chars);
+ }
+
+ if ( YY_G(yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin M4_YY_CALL_LAST_ARG);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (YY_G(yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = YY_G(yy_n_chars) + number_to_move + (YY_G(yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, new_size M4_YY_CALL_LAST_ARG );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ YY_G(yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ YY_G(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+]])
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+%if-c-only
+%not-for-header
+ static yy_state_type yy_get_previous_state YYFARGS0(void)
+%endif
+%if-c++-only
+ yy_state_type yyFlexLexer::yy_get_previous_state()
+%endif
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ M4_YY_DECL_GUTS_VAR();
+
+%% [15.0] code to get the start state into yy_current_state goes here
+
+ for ( yy_cp = YY_G(yytext_ptr) + YY_MORE_ADJ; yy_cp < YY_G(yy_c_buf_p); ++yy_cp )
+ {
+%% [16.0] code to find the next state goes here
+ }
+
+ return yy_current_state;
+}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+%if-c-only
+ static yy_state_type yy_try_NUL_trans YYFARGS1( yy_state_type, yy_current_state)
+%endif
+%if-c++-only
+ yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state )
+%endif
+{
+ register int yy_is_jam;
+ M4_YY_DECL_GUTS_VAR(); /* This var may be unused depending upon options. */
+%% [17.0] code to find the next state, and perhaps do backing up, goes here
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+
+%if-c-only
+m4_ifdef( [[M4_YY_NO_UNPUT]],,
+[[
+ static void yyunput YYFARGS2( int,c, register char *,yy_bp)
+%endif
+%if-c++-only
+ void yyFlexLexer::yyunput( int c, register char* yy_bp)
+%endif
+{
+ register char *yy_cp;
+ M4_YY_DECL_GUTS_VAR();
+
+ yy_cp = YY_G(yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = YY_G(yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = YY_G(yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ YY_G(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+%% [18.0] update yylineno here
+m4_ifdef( [[M4_YY_USE_LINENO]],
+[[
+ if ( c == '\n' ){
+ --yylineno;
+ }
+]])
+
+ YY_G(yytext_ptr) = yy_bp;
+ YY_G(yy_hold_char) = *yy_cp;
+ YY_G(yy_c_buf_p) = yy_cp;
+}
+%if-c-only
+]])
+%endif
+
+%if-c-only
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput YYFARGS0(void)
+#else
+ static int input YYFARGS0(void)
+#endif
+
+%endif
+%if-c++-only
+ int yyFlexLexer::yyinput()
+%endif
+{
+ int c;
+ M4_YY_DECL_GUTS_VAR();
+
+ *YY_G(yy_c_buf_p) = YY_G(yy_hold_char);
+
+ if ( *YY_G(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( YY_G(yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[YY_G(yy_n_chars)] )
+ /* This was really a NUL. */
+ *YY_G(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = YY_G(yy_c_buf_p) - YY_G(yytext_ptr);
+ ++YY_G(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( M4_YY_CALL_ONLY_ARG ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin M4_YY_CALL_LAST_ARG);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( M4_YY_CALL_ONLY_ARG ) )
+ return EOF;
+
+ if ( ! YY_G(yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(M4_YY_CALL_ONLY_ARG);
+#else
+ return input(M4_YY_CALL_ONLY_ARG);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ YY_G(yy_c_buf_p) = YY_G(yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) YY_G(yy_c_buf_p); /* cast for 8-bit char's */
+ *YY_G(yy_c_buf_p) = '\0'; /* preserve yytext */
+ YY_G(yy_hold_char) = *++YY_G(yy_c_buf_p);
+
+%% [19.0] update BOL and yylineno
+
+ return c;
+}
+%if-c-only
+#endif /* ifndef YY_NO_INPUT */
+%endif
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * M4_YY_DOC_PARAM
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+%if-c-only
+ void yyrestart YYFARGS1( FILE *,input_file)
+%endif
+%if-c++-only
+ void yyFlexLexer::yyrestart( std::istream* input_file )
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (M4_YY_CALL_ONLY_ARG);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE M4_YY_CALL_LAST_ARG);
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file M4_YY_CALL_LAST_ARG);
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * M4_YY_DOC_PARAM
+ */
+%if-c-only
+ void yy_switch_to_buffer YYFARGS1( YY_BUFFER_STATE ,new_buffer)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (M4_YY_CALL_ONLY_ARG);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *YY_G(yy_c_buf_p) = YY_G(yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = YY_G(yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = YY_G(yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ YY_G(yy_did_buffer_switch_on_eof) = 1;
+}
+
+
+%if-c-only
+static void yy_load_buffer_state YYFARGS0(void)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_load_buffer_state()
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ YY_G(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_G(yytext_ptr) = YY_G(yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ YY_G(yy_hold_char) = *YY_G(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * M4_YY_DOC_PARAM
+ * @return the allocated buffer state.
+ */
+%if-c-only
+ YY_BUFFER_STATE yy_create_buffer YYFARGS2( FILE *,file, int ,size)
+%endif
+%if-c++-only
+ YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( std::istream* file, int size )
+%endif
+{
+ YY_BUFFER_STATE b;
+ m4_dnl M4_YY_DECL_GUTS_VAR();
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) M4_YY_CALL_LAST_ARG );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( b->yy_buf_size + 2 M4_YY_CALL_LAST_ARG );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file M4_YY_CALL_LAST_ARG);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * M4_YY_DOC_PARAM
+ */
+%if-c-only
+ void yy_delete_buffer YYFARGS1( YY_BUFFER_STATE ,b)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b )
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf M4_YY_CALL_LAST_ARG );
+
+ yyfree( (void *) b M4_YY_CALL_LAST_ARG );
+}
+
+
+%if-c-only
+m4_ifdef( [[M4_YY_ALWAYS_INTERACTIVE]],,
+[[
+ m4_ifdef( [[M4_YY_NEVER_INTERACTIVE]],,
+ [[
+#ifndef __cplusplus
+extern int isatty M4_YY_PARAMS( int );
+#endif /* __cplusplus */
+ ]])
+]])
+%endif
+
+%if-c++-only
+m4_ifdef( [[M4_YY_NEVER_INTERACTIVE]],,
+[[
+]])
+%endif
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+%if-c-only
+ static void yy_init_buffer YYFARGS2( YY_BUFFER_STATE ,b, FILE *,file)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, std::istream* file )
+%endif
+
+{
+ int oerrno = errno;
+ M4_YY_DECL_GUTS_VAR();
+
+ yy_flush_buffer( b M4_YY_CALL_LAST_ARG);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+%if-c-only
+m4_ifdef( [[M4_YY_ALWAYS_INTERACTIVE]],
+[[
+ b->yy_is_interactive = 1;
+]],
+[[
+ m4_ifdef( [[M4_YY_NEVER_INTERACTIVE]],
+ [[
+ b->yy_is_interactive = 0;
+ ]],
+ [[
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+ ]])
+]])
+%endif
+%if-c++-only
+ b->yy_is_interactive = 0;
+%endif
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * M4_YY_DOC_PARAM
+ */
+%if-c-only
+ void yy_flush_buffer YYFARGS1( YY_BUFFER_STATE ,b)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b )
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+}
+
+%if-c-or-c++
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * M4_YY_DOC_PARAM
+ */
+%if-c-only
+void yypush_buffer_state YYFARGS1(YY_BUFFER_STATE,new_buffer)
+%endif
+%if-c++-only
+void yyFlexLexer::yypush_buffer_state (YY_BUFFER_STATE new_buffer)
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(M4_YY_CALL_ONLY_ARG);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *YY_G(yy_c_buf_p) = YY_G(yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = YY_G(yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = YY_G(yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ YY_G(yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+ YY_G(yy_did_buffer_switch_on_eof) = 1;
+}
+%endif
+
+
+%if-c-or-c++
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * M4_YY_DOC_PARAM
+ */
+%if-c-only
+void yypop_buffer_state YYFARGS0(void)
+%endif
+%if-c++-only
+void yyFlexLexer::yypop_buffer_state (void)
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER M4_YY_CALL_LAST_ARG);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (YY_G(yy_buffer_stack_top) > 0)
+ --YY_G(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( M4_YY_CALL_ONLY_ARG );
+ YY_G(yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+%endif
+
+
+%if-c-or-c++
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+%if-c-only
+static void yyensure_buffer_stack YYFARGS0(void)
+%endif
+%if-c++-only
+void yyFlexLexer::yyensure_buffer_stack(void)
+%endif
+{
+ int num_to_alloc;
+ M4_YY_DECL_GUTS_VAR();
+
+ if (!YY_G(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ YY_G(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ M4_YY_CALL_LAST_ARG);
+ if ( ! YY_G(yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+
+ memset(YY_G(yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ YY_G(yy_buffer_stack_max) = num_to_alloc;
+ YY_G(yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if (YY_G(yy_buffer_stack_top) >= (YY_G(yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = YY_G(yy_buffer_stack_max) + grow_size;
+ YY_G(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ (YY_G(yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ M4_YY_CALL_LAST_ARG);
+ if ( ! YY_G(yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(YY_G(yy_buffer_stack) + YY_G(yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ YY_G(yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+%endif
+
+
+
+
+m4_ifdef( [[M4_YY_NO_SCAN_BUFFER]],,
+[[
+%if-c-only
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * M4_YY_DOC_PARAM
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer YYFARGS2( char *,base, yy_size_t ,size)
+{
+ YY_BUFFER_STATE b;
+ m4_dnl M4_YY_DECL_GUTS_VAR();
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) M4_YY_CALL_LAST_ARG );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b M4_YY_CALL_LAST_ARG );
+
+ return b;
+}
+%endif
+]])
+
+
+m4_ifdef( [[M4_YY_NO_SCAN_STRING]],,
+[[
+%if-c-only
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * M4_YY_DOC_PARAM
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string YYFARGS1( yyconst char *, yystr)
+{
+ m4_dnl M4_YY_DECL_GUTS_VAR();
+
+ return yy_scan_bytes( yystr, strlen(yystr) M4_YY_CALL_LAST_ARG);
+}
+%endif
+]])
+
+
+m4_ifdef( [[M4_YY_NO_SCAN_BYTES]],,
+[[
+%if-c-only
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * M4_YY_DOC_PARAM
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes YYFARGS2( yyconst char *,yybytes, int ,_yybytes_len)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+ m4_dnl M4_YY_DECL_GUTS_VAR();
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc( n M4_YY_CALL_LAST_ARG );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n M4_YY_CALL_LAST_ARG);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+%endif
+]])
+
+
+m4_ifdef( [[M4_YY_NO_PUSH_STATE]],,
+[[
+%if-c-only
+ static void yy_push_state YYFARGS1( int ,new_state)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_push_state( int new_state )
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ if ( YY_G(yy_start_stack_ptr) >= YY_G(yy_start_stack_depth) )
+ {
+ yy_size_t new_size;
+
+ YY_G(yy_start_stack_depth) += YY_START_STACK_INCR;
+ new_size = YY_G(yy_start_stack_depth) * sizeof( int );
+
+ if ( ! YY_G(yy_start_stack) )
+ YY_G(yy_start_stack) = (int *) yyalloc( new_size M4_YY_CALL_LAST_ARG );
+
+ else
+ YY_G(yy_start_stack) = (int *) yyrealloc(
+ (void *) YY_G(yy_start_stack), new_size M4_YY_CALL_LAST_ARG );
+
+ if ( ! YY_G(yy_start_stack) )
+ YY_FATAL_ERROR( "out of memory expanding start-condition stack" );
+ }
+
+ YY_G(yy_start_stack)[YY_G(yy_start_stack_ptr)++] = YY_START;
+
+ BEGIN(new_state);
+}
+]])
+
+
+m4_ifdef( [[M4_YY_NO_POP_STATE]],,
+[[
+%if-c-only
+ static void yy_pop_state YYFARGS0(void)
+%endif
+%if-c++-only
+ void yyFlexLexer::yy_pop_state()
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ if ( --YY_G(yy_start_stack_ptr) < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(YY_G(yy_start_stack)[YY_G(yy_start_stack_ptr)]);
+}
+]])
+
+
+m4_ifdef( [[M4_YY_NO_TOP_STATE]],,
+[[
+%if-c-only
+ static int yy_top_state YYFARGS0(void)
+%endif
+%if-c++-only
+ int yyFlexLexer::yy_top_state()
+%endif
+{
+ M4_YY_DECL_GUTS_VAR();
+ return YY_G(yy_start_stack)[YY_G(yy_start_stack_ptr) - 1];
+}
+]])
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+%if-c-only
+static void yy_fatal_error YYFARGS1(yyconst char*, msg)
+{
+ m4_dnl M4_YY_DECL_GUTS_VAR();
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+%endif
+%if-c++-only
+void yyFlexLexer::LexerError( yyconst char msg[] )
+{
+ M4_YY_DECL_GUTS_VAR();
+ std::cerr << msg << std::endl;
+ exit( YY_EXIT_FAILURE );
+}
+%endif
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = YY_G(yy_hold_char); \
+ YY_G(yy_c_buf_p) = yytext + yyless_macro_arg; \
+ YY_G(yy_hold_char) = *YY_G(yy_c_buf_p); \
+ *YY_G(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+
+
+/* Accessor methods (get/set functions) to struct members. */
+
+%if-c-only
+%if-reentrant
+m4_ifdef( [[M4_YY_NO_GET_EXTRA]],,
+[[
+/** Get the user-defined data for this scanner.
+ * M4_YY_DOC_PARAM
+ */
+YY_EXTRA_TYPE yyget_extra YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yyextra;
+}
+]])
+%endif
+
+m4_ifdef( [[M4_YY_NO_GET_LINENO]],,
+[[
+/** Get the current line number.
+ * M4_YY_DOC_PARAM
+ */
+int yyget_lineno YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ m4_ifdef( [[M4_YY_REENTRANT]],
+ [[
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+ ]])
+ return yylineno;
+}
+]])
+
+m4_ifdef( [[M4_YY_REENTRANT]],
+[[
+m4_ifdef( [[M4_YY_NO_GET_COLUMN]],,
+[[
+/** Get the current column number.
+ * M4_YY_DOC_PARAM
+ */
+int yyget_column YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ m4_ifdef( [[M4_YY_REENTRANT]],
+ [[
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+ ]])
+ return yycolumn;
+}
+]])
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_IN]],,
+[[
+/** Get the input stream.
+ * M4_YY_DOC_PARAM
+ */
+FILE *yyget_in YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yyin;
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_OUT]],,
+[[
+/** Get the output stream.
+ * M4_YY_DOC_PARAM
+ */
+FILE *yyget_out YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yyout;
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_GET_LENG]],,
+[[
+/** Get the length of the current token.
+ * M4_YY_DOC_PARAM
+ */
+int yyget_leng YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yyleng;
+}
+]])
+
+/** Get the current token.
+ * M4_YY_DOC_PARAM
+ */
+m4_ifdef( [[M4_YY_NO_GET_TEXT]],,
+[[
+char *yyget_text YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yytext;
+}
+]])
+
+%if-reentrant
+m4_ifdef( [[M4_YY_NO_SET_EXTRA]],,
+[[
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * M4_YY_DOC_PARAM
+ */
+void yyset_extra YYFARGS1( YY_EXTRA_TYPE ,user_defined)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yyextra = user_defined ;
+}
+]])
+%endif
+
+m4_ifdef( [[M4_YY_NO_SET_LINENO]],,
+[[
+/** Set the current line number.
+ * @param line_number
+ * M4_YY_DOC_PARAM
+ */
+void yyset_lineno YYFARGS1( int ,line_number)
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ m4_ifdef( [[M4_YY_REENTRANT]],
+ [[
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "yyset_lineno called with no buffer" M4_YY_CALL_LAST_ARG);
+ ]])
+ yylineno = line_number;
+}
+]])
+
+m4_ifdef( [[M4_YY_REENTRANT]],
+[[
+m4_ifdef( [[M4_YY_NO_SET_COLUMN]],,
+[[
+/** Set the current column.
+ * @param line_number
+ * M4_YY_DOC_PARAM
+ */
+void yyset_column YYFARGS1( int , column_no)
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ m4_ifdef( [[M4_YY_REENTRANT]],
+ [[
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "yyset_column called with no buffer" M4_YY_CALL_LAST_ARG);
+ ]])
+ yycolumn = column_no;
+}
+]])
+]])
+
+
+m4_ifdef( [[M4_YY_NO_SET_IN]],,
+[[
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * M4_YY_DOC_PARAM
+ * @see yy_switch_to_buffer
+ */
+void yyset_in YYFARGS1( FILE * ,in_str)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yyin = in_str ;
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_OUT]],,
+[[
+void yyset_out YYFARGS1( FILE * ,out_str)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yyout = out_str ;
+}
+]])
+
+
+m4_ifdef( [[M4_YY_NO_GET_DEBUG]],,
+[[
+int yyget_debug YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yy_flex_debug;
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_DEBUG]],,
+[[
+void yyset_debug YYFARGS1( int ,bdebug)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yy_flex_debug = bdebug ;
+}
+]])
+%endif
+
+%if-reentrant
+/* Accessor methods for yylval and yylloc */
+
+%if-bison-bridge
+m4_ifdef( [[M4_YY_NO_GET_LVAL]],,
+[[
+YYSTYPE * yyget_lval YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yylval;
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_SET_LVAL]],,
+[[
+void yyset_lval YYFARGS1( YYSTYPE * ,yylval_param)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yylval = yylval_param;
+}
+]])
+
+m4_ifdef( [[<M4_YY_BISON_LLOC>]],
+[[
+ m4_ifdef( [[M4_YY_NO_GET_LLOC]],,
+ [[
+YYLTYPE *yyget_lloc YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ return yylloc;
+}
+ ]])
+
+ m4_ifdef( [[M4_YY_NO_SET_LLOC]],,
+ [[
+void yyset_lloc YYFARGS1( YYLTYPE * ,yylloc_param)
+{
+ M4_YY_DECL_GUTS_VAR();
+ yylloc = yylloc_param;
+}
+ ]])
+]])
+
+%endif
+
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_DEFS]],
+[[
+int yylex_init( ptr_yy_globals )
+ yyscan_t* ptr_yy_globals;
+]],
+[[
+int yylex_init(yyscan_t* ptr_yy_globals)
+]])
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+m4_ifdef( [[M4_YY_NO_ANSI_FUNC_DEFS]],
+[[
+int yylex_init_extra( yy_user_defined, ptr_yy_globals )
+ YY_EXTRA_TYPE yy_user_defined;
+ yyscan_t* ptr_yy_globals;
+]],
+[[
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+]])
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+%endif if-c-only
+
+
+%if-c-only
+static int yy_init_globals YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+m4_ifdef( [[M4_YY_USE_LINENO]],
+[[
+ m4_ifdef( [[M4_YY_NOT_REENTRANT]],
+ [[
+ /* We do not touch yylineno unless the option is enabled. */
+ yylineno = 1;
+ ]])
+]])
+ YY_G(yy_buffer_stack) = 0;
+ YY_G(yy_buffer_stack_top) = 0;
+ YY_G(yy_buffer_stack_max) = 0;
+ YY_G(yy_c_buf_p) = (char *) 0;
+ YY_G(yy_init) = 0;
+ YY_G(yy_start) = 0;
+
+m4_ifdef( [[M4_YY_HAS_START_STACK_VARS]],
+[[
+ YY_G(yy_start_stack_ptr) = 0;
+ YY_G(yy_start_stack_depth) = 0;
+ YY_G(yy_start_stack) = NULL;
+]])
+
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ YY_G(yy_state_buf) = 0;
+ YY_G(yy_state_ptr) = 0;
+ YY_G(yy_full_match) = 0;
+ YY_G(yy_lp) = 0;
+]])
+
+m4_ifdef( [[M4_YY_TEXT_IS_ARRAY]],
+[[
+ YY_G(yytext_ptr) = 0;
+ YY_G(yy_more_offset) = 0;
+ YY_G(yy_prev_more_offset) = 0;
+]])
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+%endif
+
+
+%if-c-only SNIP! this currently causes conflicts with the c++ scanner
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy YYFARGS0(void)
+{
+ M4_YY_DECL_GUTS_VAR();
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER M4_YY_CALL_LAST_ARG );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(M4_YY_CALL_ONLY_ARG);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(YY_G(yy_buffer_stack) M4_YY_CALL_LAST_ARG);
+ YY_G(yy_buffer_stack) = NULL;
+
+m4_ifdef( [[M4_YY_HAS_START_STACK_VARS]],
+[[
+ /* Destroy the start condition stack. */
+ yyfree( YY_G(yy_start_stack) M4_YY_CALL_LAST_ARG );
+ YY_G(yy_start_stack) = NULL;
+]])
+
+m4_ifdef( [[M4_YY_USES_REJECT]],
+[[
+ yyfree ( YY_G(yy_state_buf) M4_YY_CALL_LAST_ARG);
+ YY_G(yy_state_buf) = NULL;
+]])
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( M4_YY_CALL_ONLY_ARG);
+
+%if-reentrant
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner M4_YY_CALL_LAST_ARG );
+ yyscanner = NULL;
+%endif
+ return 0;
+}
+%endif
+
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+/*
+ * Internal utility routines.
+ */
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#ifndef yytext_ptr
+static void yy_flex_strncpy YYFARGS3( char*,s1, yyconst char *,s2, int,n)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NOT_IN_HEADER]],
+[[
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YYFARGS1( yyconst char *,s)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+]])
+
+m4_ifdef( [[M4_YY_NO_FLEX_ALLOC]],,
+[[
+void *yyalloc YYFARGS1( yy_size_t ,size)
+{
+ return (void *) malloc( size );
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_FLEX_REALLOC]],,
+[[
+void *yyrealloc YYFARGS2( void *,ptr, yy_size_t ,size)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+]])
+
+m4_ifdef( [[M4_YY_NO_FLEX_FREE]],,
+[[
+void yyfree YYFARGS1( void *,ptr)
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+]])
+
+%if-tables-serialization definitions
+m4preproc_include(`tables_shared.c')
+
+static int yytbl_read8 (void *v, struct yytbl_reader * rd)
+{
+ errno = 0;
+ if (fread (v, sizeof (flex_uint8_t), 1, rd->fp) != 1){
+ errno = EIO;
+ return -1;
+ }
+ rd->bread += sizeof(flex_uint8_t);
+ return 0;
+}
+
+static int yytbl_read16 (void *v, struct yytbl_reader * rd)
+{
+ errno = 0;
+ if (fread (v, sizeof (flex_uint16_t), 1, rd->fp) != 1){
+ errno = EIO;
+ return -1;
+ }
+ *((flex_uint16_t *) v) = ntohs (*((flex_uint16_t *) v));
+ rd->bread += sizeof(flex_uint16_t);
+ return 0;
+}
+
+static int yytbl_read32 (void *v, struct yytbl_reader * rd)
+{
+ errno = 0;
+ if (fread (v, sizeof (flex_uint32_t), 1, rd->fp) != 1){
+ errno = EIO;
+ return -1;
+ }
+ *((flex_uint32_t *) v) = ntohl (*((flex_uint32_t *) v));
+ rd->bread += sizeof(flex_uint32_t);
+ return 0;
+}
+
+/** Read the header */
+static int yytbl_hdr_read YYFARGS2(struct yytbl_hdr *, th, struct yytbl_reader *, rd)
+{
+ int bytes;
+ memset (th, 0, sizeof (struct yytbl_hdr));
+
+ if (yytbl_read32 (&(th->th_magic), rd) != 0)
+ return -1;
+
+ if (th->th_magic != YYTBL_MAGIC){
+ yy_fatal_error("bad magic number" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+
+ if (yytbl_read32 (&(th->th_hsize), rd) != 0
+ || yytbl_read32 (&(th->th_ssize), rd) != 0
+ || yytbl_read16 (&(th->th_flags), rd) != 0)
+ return -1;
+
+ /* Sanity check on header size. Greater than 1k suggests some funny business. */
+ if (th->th_hsize < 16 || th->th_hsize > 1024){
+ yy_fatal_error("insane header size detected" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+
+ /* Allocate enough space for the version and name fields */
+ bytes = th->th_hsize - 14;
+ th->th_version = (char *) yyalloc (bytes M4_YY_CALL_LAST_ARG);
+ if ( ! th->th_version )
+ YY_FATAL_ERROR( "out of dynamic memory in yytbl_hdr_read()" );
+
+ /* we read it all into th_version, and point th_name into that data */
+ if (fread (th->th_version, 1, bytes, rd->fp) != bytes){
+ errno = EIO;
+ yyfree(th->th_version M4_YY_CALL_LAST_ARG);
+ th->th_version = NULL;
+ return -1;
+ }
+ else
+ rd->bread += bytes;
+
+ th->th_name = th->th_version + strlen (th->th_version) + 1;
+ return 0;
+}
+
+/** lookup id in the dmap list.
+ * @param dmap pointer to first element in list
+ * @return NULL if not found.
+ */
+static struct yytbl_dmap *yytbl_dmap_lookup YYFARGS2(struct yytbl_dmap *, dmap,
+ int, id)
+{
+ while (dmap->dm_id)
+ if (dmap->dm_id == id)
+ return dmap;
+ else
+ dmap++;
+ return NULL;
+}
+
+/** Read a table while mapping its contents to the local array.
+ * @param dmap used to performing mapping
+ * @return 0 on success
+ */
+static int yytbl_data_load YYFARGS2(struct yytbl_dmap *, dmap, struct yytbl_reader*, rd)
+{
+ struct yytbl_data td;
+ struct yytbl_dmap *transdmap=0;
+ int len, i, rv, inner_loop_count;
+ void *p=0;
+
+ memset (&td, 0, sizeof (struct yytbl_data));
+
+ if (yytbl_read16 (&td.td_id, rd) != 0
+ || yytbl_read16 (&td.td_flags, rd) != 0
+ || yytbl_read32 (&td.td_hilen, rd) != 0
+ || yytbl_read32 (&td.td_lolen, rd) != 0)
+ return -1;
+
+ /* Lookup the map for the transition table so we have it in case we need it
+ * inside the loop below. This scanner might not even have a transition
+ * table, which is ok.
+ */
+ transdmap = yytbl_dmap_lookup (dmap, YYTD_ID_TRANSITION M4_YY_CALL_LAST_ARG);
+
+ if ((dmap = yytbl_dmap_lookup (dmap, td.td_id M4_YY_CALL_LAST_ARG)) == NULL){
+ yy_fatal_error("table id not found in map." /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+
+ /* Allocate space for table.
+ * The --full yy_transition table is a special case, since we
+ * need the dmap.dm_sz entry to tell us the sizeof the individual
+ * struct members.
+ */
+ {
+ size_t bytes;
+
+ if ((td.td_flags & YYTD_STRUCT))
+ bytes = sizeof(struct yy_trans_info) * td.td_lolen * (td.td_hilen ? td.td_hilen : 1);
+ else
+ bytes = td.td_lolen * (td.td_hilen ? td.td_hilen : 1) * dmap->dm_sz;
+
+ if(M4_YY_TABLES_VERIFY)
+ /* We point to the array itself */
+ p = dmap->dm_arr;
+ else
+ /* We point to the address of a pointer. */
+ *dmap->dm_arr = p = (void *) yyalloc (bytes M4_YY_CALL_LAST_ARG);
+ if ( ! p )
+ YY_FATAL_ERROR( "out of dynamic memory in yytbl_data_load()" );
+ }
+
+ /* If it's a struct, we read 2 integers to get one element */
+ if ((td.td_flags & YYTD_STRUCT) != 0)
+ inner_loop_count = 2;
+ else
+ inner_loop_count = 1;
+
+ /* read and map each element.
+ * This loop iterates once for each element of the td_data array.
+ * Notice that we increment 'i' in the inner loop.
+ */
+ len = yytbl_calc_total_len (&td);
+ for (i = 0; i < len; ){
+ int j;
+
+
+ /* This loop really executes exactly 1 or 2 times.
+ * The second time is to handle the second member of the
+ * YYTD_STRUCT for the yy_transition array.
+ */
+ for (j = 0; j < inner_loop_count; j++, i++) {
+ flex_int32_t t32;
+
+ /* read into t32 no matter what the real size is. */
+ {
+ flex_int16_t t16;
+ flex_int8_t t8;
+
+ switch (YYTDFLAGS2BYTES (td.td_flags)) {
+ case sizeof (flex_int32_t):
+ rv = yytbl_read32 (&t32, rd);
+ break;
+ case sizeof (flex_int16_t):
+ rv = yytbl_read16 (&t16, rd);
+ t32 = t16;
+ break;
+ case sizeof (flex_int8_t):
+ rv = yytbl_read8 (&t8, rd);
+ t32 = t8;
+ break;
+ default:
+ yy_fatal_error("invalid td_flags" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+ }
+ if (rv != 0)
+ return -1;
+
+ /* copy into the deserialized array... */
+
+ if ((td.td_flags & YYTD_STRUCT)) {
+ /* t32 is the j'th member of a two-element struct. */
+ void *v;
+
+ v = j == 0 ? &(((struct yy_trans_info *) p)->yy_verify)
+ : &(((struct yy_trans_info *) p)->yy_nxt);
+
+ switch (dmap->dm_sz) {
+ case sizeof (flex_int32_t):
+ if (M4_YY_TABLES_VERIFY){
+ if( ((flex_int32_t *) v)[0] != (flex_int32_t) t32)
+ yy_fatal_error("tables verification failed at YYTD_STRUCT flex_int32_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int32_t *) v)[0] = (flex_int32_t) t32;
+ break;
+ case sizeof (flex_int16_t):
+ if (M4_YY_TABLES_VERIFY ){
+ if(((flex_int16_t *) v)[0] != (flex_int16_t) t32)
+ yy_fatal_error("tables verification failed at YYTD_STRUCT flex_int16_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int16_t *) v)[0] = (flex_int16_t) t32;
+ break;
+ case sizeof(flex_int8_t):
+ if (M4_YY_TABLES_VERIFY ){
+ if( ((flex_int8_t *) v)[0] != (flex_int8_t) t32)
+ yy_fatal_error("tables verification failed at YYTD_STRUCT flex_int8_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int8_t *) v)[0] = (flex_int8_t) t32;
+ break;
+ default:
+ yy_fatal_error("invalid dmap->dm_sz for struct" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+
+ /* if we're done with j, increment p */
+ if (j == 1)
+ p = (struct yy_trans_info *) p + 1;
+ }
+ else if ((td.td_flags & YYTD_PTRANS)) {
+ /* t32 is an index into the transition array. */
+ struct yy_trans_info *v;
+
+
+ if (!transdmap){
+ yy_fatal_error("transition table not found" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+
+ if( M4_YY_TABLES_VERIFY)
+ v = &(((struct yy_trans_info *) (transdmap->dm_arr))[t32]);
+ else
+ v = &((*((struct yy_trans_info **) (transdmap->dm_arr)))[t32]);
+
+ if(M4_YY_TABLES_VERIFY ){
+ if( ((struct yy_trans_info **) p)[0] != v)
+ yy_fatal_error("tables verification failed at YYTD_PTRANS" M4_YY_CALL_LAST_ARG);
+ }else
+ ((struct yy_trans_info **) p)[0] = v;
+
+ /* increment p */
+ p = (struct yy_trans_info **) p + 1;
+ }
+ else {
+ /* t32 is a plain int. copy data, then incrememnt p. */
+ switch (dmap->dm_sz) {
+ case sizeof (flex_int32_t):
+ if(M4_YY_TABLES_VERIFY ){
+ if( ((flex_int32_t *) p)[0] != (flex_int32_t) t32)
+ yy_fatal_error("tables verification failed at flex_int32_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int32_t *) p)[0] = (flex_int32_t) t32;
+ p = ((flex_int32_t *) p) + 1;
+ break;
+ case sizeof (flex_int16_t):
+ if(M4_YY_TABLES_VERIFY ){
+ if( ((flex_int16_t *) p)[0] != (flex_int16_t) t32)
+ yy_fatal_error("tables verification failed at flex_int16_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int16_t *) p)[0] = (flex_int16_t) t32;
+ p = ((flex_int16_t *) p) + 1;
+ break;
+ case sizeof (flex_int8_t):
+ if(M4_YY_TABLES_VERIFY ){
+ if( ((flex_int8_t *) p)[0] != (flex_int8_t) t32)
+ yy_fatal_error("tables verification failed at flex_int8_t" M4_YY_CALL_LAST_ARG);
+ }else
+ ((flex_int8_t *) p)[0] = (flex_int8_t) t32;
+ p = ((flex_int8_t *) p) + 1;
+ break;
+ default:
+ yy_fatal_error("invalid dmap->dm_sz for plain int" /*TODO: not fatal.*/ M4_YY_CALL_LAST_ARG);
+ return -1;
+ }
+ }
+ }
+
+ }
+
+ /* Now eat padding. */
+ {
+ int pad;
+ pad = yypad64(rd->bread);
+ while(--pad >= 0){
+ flex_int8_t t8;
+ if(yytbl_read8(&t8,rd) != 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+%define-yytables The name for this specific scanner's tables.
+
+/* Find the key and load the DFA tables from the given stream. */
+static int yytbl_fload YYFARGS2(FILE *, fp, const char *, key)
+{
+ int rv=0;
+ struct yytbl_hdr th;
+ struct yytbl_reader rd;
+
+ rd.fp = fp;
+ th.th_version = NULL;
+
+ /* Keep trying until we find the right set of tables or end of file. */
+ while (!feof(rd.fp)) {
+ rd.bread = 0;
+ if (yytbl_hdr_read (&th, &rd M4_YY_CALL_LAST_ARG) != 0){
+ rv = -1;
+ goto return_rv;
+ }
+
+ /* A NULL key means choose the first set of tables. */
+ if (key == NULL)
+ break;
+
+ if (strcmp(th.th_name,key) != 0){
+ /* Skip ahead to next set */
+ fseek(rd.fp, th.th_ssize - th.th_hsize, SEEK_CUR);
+ yyfree(th.th_version M4_YY_CALL_LAST_ARG);
+ th.th_version = NULL;
+ }
+ else
+ break;
+ }
+
+ while (rd.bread < th.th_ssize){
+ /* Load the data tables */
+ if(yytbl_data_load (yydmap,&rd M4_YY_CALL_LAST_ARG) != 0){
+ rv = -1;
+ goto return_rv;
+ }
+ }
+
+return_rv:
+ if(th.th_version){
+ yyfree(th.th_version M4_YY_CALL_LAST_ARG);
+ th.th_version = NULL;
+ }
+
+ return rv;
+}
+
+/** Load the DFA tables for this scanner from the given stream. */
+int yytables_fload YYFARGS1(FILE *, fp)
+{
+
+ if( yytbl_fload(fp, YYTABLES_NAME M4_YY_CALL_LAST_ARG) != 0)
+ return -1;
+ return 0;
+}
+
+/** Destroy the loaded tables, freeing memory, etc.. */
+int yytables_destroy YYFARGS0(void)
+{
+ struct yytbl_dmap *dmap=0;
+
+ if(!M4_YY_TABLES_VERIFY){
+ /* Walk the dmap, freeing the pointers */
+ for(dmap=yydmap; dmap->dm_id; dmap++) {
+ void * v;
+ v = dmap->dm_arr;
+ if(v && *(char**)v){
+ yyfree(*(char**)v M4_YY_CALL_LAST_ARG);
+ *(char**)v = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* end table serialization code definitions */
+%endif
+
+
+m4_ifdef([[M4_YY_MAIN]], [[
+int main M4_YY_PARAMS(void);
+
+int main ()
+{
+
+%if-reentrant
+ yyscan_t lexer;
+ yylex_init(&lexer);
+ yylex( lexer );
+ yylex_destroy( lexer);
+
+%endif
+%if-not-reentrant
+ yylex();
+%endif
+
+ return 0;
+}
+]])
+
+%ok-for-header
+m4_ifdef( [[M4_YY_IN_HEADER]],
+[[
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+]])
diff --git a/tools/mhmake/src/flexint.h b/tools/mhmake/src/flexint.h
new file mode 100644
index 000000000..673aa1757
--- /dev/null
+++ b/tools/mhmake/src/flexint.h
@@ -0,0 +1,63 @@
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+
+#endif /* ! FLEXINT_H */
diff --git a/tools/mhmake/src/flexlexer.h b/tools/mhmake/src/flexlexer.h
new file mode 100644
index 000000000..48dab04ae
--- /dev/null
+++ b/tools/mhmake/src/flexlexer.h
@@ -0,0 +1,260 @@
+// -*-C++-*-
+// FlexLexer.h -- define interfaces for lexical analyzer classes generated
+// by flex
+
+// Copyright (c) 1993 The Regents of the University of California.
+// All rights reserved.
+//
+// This code is derived from software contributed to Berkeley by
+// Kent Williams and Tom Epperly.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+
+// Neither the name of the University nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+// This file defines FlexLexer, an abstract class which specifies the
+// external interface provided to flex C++ lexer objects, and yyFlexLexer,
+// which defines a particular lexer class.
+//
+// If you want to create multiple lexer classes, you use the -P flag
+// to rename each yyFlexLexer to some other xxFlexLexer. You then
+// include <FlexLexer.h> in your other sources once per lexer class:
+//
+// #undef yyFlexLexer
+// #define yyFlexLexer xxFlexLexer
+// #include <FlexLexer.h>
+//
+// #undef yyFlexLexer
+// #define yyFlexLexer zzFlexLexer
+// #include <FlexLexer.h>
+// ...
+
+#ifndef __FLEX_LEXER_H
+// Never included before - need to define base class.
+#define __FLEX_LEXER_H
+#include "mhmakeparser.h"
+
+#include <iostream>
+# ifndef FLEX_STD
+# define FLEX_STD std::
+# endif
+
+extern "C++" {
+
+struct yy_buffer_state;
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+typedef int yy_state_type;
+
+class FlexLexer {
+public:
+ FlexLexer() : yylineno(1), m_BraceIndent(0) {}
+ virtual ~FlexLexer() { }
+
+ const char* YYText() const { return yytext; }
+ int YYLeng() const { return yyleng; }
+
+ virtual void
+ yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0;
+ virtual struct yy_buffer_state*
+ yy_create_buffer( FLEX_STD istream* s, int size ) = 0;
+ virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0;
+ virtual void yyrestart( FLEX_STD istream* s ) = 0;
+
+ virtual int yylex(TOKENVALUE &theValue) = 0;
+
+
+ // Switch to new input/output streams. A nil stream pointer
+ // indicates "keep the current one".
+ virtual void switch_streams( FLEX_STD istream* new_in = 0,
+ FLEX_STD ostream* new_out = 0 ) = 0;
+
+ int lineno() const { return yylineno; }
+
+ int debug() const { return yy_flex_debug; }
+ void set_debug( int flag ) { yy_flex_debug = flag; }
+
+protected:
+ char* yytext;
+ int yyleng;
+ int yylineno; // only maintained if you use %option yylineno
+ int yy_flex_debug; // only has effect with -d or "%option debug"
+
+public:
+
+ struct INSTACK
+ {
+ YY_BUFFER_STATE m_BufferState;
+ ifstream m_Stream;
+ string m_FileName;
+ int yylineno;
+ INSTACK(YY_BUFFER_STATE BufferState, const string &FileToOpen, const string &PrevFileName, int Line) :
+ m_BufferState(BufferState),
+ m_FileName(PrevFileName),
+ yylineno(Line),
+ m_Stream(FileToOpen.c_str(), ios_base::in)
+ {
+ }
+ bool fail() const
+ {
+ return m_Stream.fail();
+ }
+ ifstream *GetStream()
+ {
+ return &m_Stream;
+ }
+ };
+ class mystack: public iterstack<INSTACK*>
+ {
+ public:
+ ~mystack()
+ {
+ iterator It=begin();
+ while (It!=end())
+ {
+ delete *It;
+ It++;
+ }
+ }
+ void pop()
+ {
+ INSTACK *pToRemove=top();
+ iterstack<INSTACK*>::pop();
+ delete pToRemove; /* Buffer does not need to be deleted because the lexer does this when it calls the pop */
+ }
+ };
+ int m_BraceIndent;
+ size_t m_IndentSkip;
+ iterstack<int> m_IndentStack;
+ bool m_IgnoreIncludeError;
+ string m_InputFileName;
+ string m_curtoken;
+ mystack m_IncludeStack;
+ mhmakeparser *m_pParser;
+ mhmakeparser *GetParser()
+ {
+ return m_pParser;
+ }
+
+};
+
+}
+#endif // FLEXLEXER_H
+
+#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce)
+// Either this is the first time through (yyFlexLexerOnce not defined),
+// or this is a repeated include to define a different flavor of
+// yyFlexLexer, as discussed in the flex manual.
+#define yyFlexLexerOnce
+
+extern "C++" {
+
+class mhmakeFlexLexer : public FlexLexer {
+public:
+ // arg_yyin and arg_yyout default to the cin and cout, but we
+ // only make that assignment when initializing in yylex().
+ mhmakeFlexLexer( FLEX_STD istream* arg_yyin = 0, FLEX_STD ostream* arg_yyout = 0 );
+
+ virtual ~mhmakeFlexLexer();
+
+ void yy_switch_to_buffer( struct yy_buffer_state* new_buffer );
+ struct yy_buffer_state* yy_create_buffer( FLEX_STD istream* s, int size );
+ void yy_delete_buffer( struct yy_buffer_state* b );
+ void yyrestart( FLEX_STD istream* s );
+
+ void yypush_buffer_state( struct yy_buffer_state* new_buffer );
+ void yypop_buffer_state();
+
+ virtual int yylex(TOKENVALUE &theValue);
+ virtual void switch_streams( FLEX_STD istream* new_in, FLEX_STD ostream* new_out = 0 );
+
+protected:
+ virtual int LexerInput( char* buf, int max_size );
+ virtual void LexerOutput( const char* buf, int size );
+ virtual void LexerError( const char* msg );
+
+ void yyunput( int c, char* buf_ptr );
+ int yyinput();
+ int yywrap();
+
+ void yy_load_buffer_state();
+ void yy_init_buffer( struct yy_buffer_state* b, FLEX_STD istream* s );
+ void yy_flush_buffer( struct yy_buffer_state* b );
+
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int* yy_start_stack;
+
+ void yy_push_state( int new_state );
+ void yy_pop_state();
+ int yy_top_state();
+
+ yy_state_type yy_get_previous_state();
+ yy_state_type yy_try_NUL_trans( yy_state_type current_state );
+ int yy_get_next_buffer();
+
+ FLEX_STD istream* yyin; // input source for default LexerInput
+ FLEX_STD ostream* yyout; // output sink for default LexerOutput
+
+ // yy_hold_char holds the character lost when yytext is formed.
+ char yy_hold_char;
+
+ // Number of characters read into yy_ch_buf.
+ int yy_n_chars;
+
+ // Points to current character in buffer.
+ char* yy_c_buf_p;
+
+ int yy_init; // whether we need to initialize
+ int yy_start; // start state number
+
+ // Flag which is used to allow yywrap()'s to do buffer switches
+ // instead of setting up a fresh yyin. A bit of a hack ...
+ int yy_did_buffer_switch_on_eof;
+
+
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ struct yy_buffer_state ** yy_buffer_stack; /**< Stack as an array. */
+ void yyensure_buffer_stack(void);
+
+ // The following are not always needed, but may be depending
+ // on use of certain flex features (like REJECT or yymore()).
+
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ yy_state_type* yy_state_buf;
+ yy_state_type* yy_state_ptr;
+
+ char* yy_full_match;
+ int* yy_full_state;
+ int yy_full_lp;
+
+ int yy_lp;
+ int yy_looking_for_trail_begin;
+
+ int yy_more_flag;
+ int yy_more_len;
+ int yy_more_offset;
+ int yy_prev_more_offset;
+};
+
+}
+
+#endif // yyFlexLexer || ! yyFlexLexerOnce
+
diff --git a/tools/mhmake/src/functions.cpp b/tools/mhmake/src/functions.cpp
new file mode 100644
index 000000000..61ae6c1e4
--- /dev/null
+++ b/tools/mhmake/src/functions.cpp
@@ -0,0 +1,842 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "util.h"
+#include "mhmakefileparser.h"
+#include "rule.h"
+
+static const string s_QuoteString("\"");
+
+funcdef mhmakefileparser::m_FunctionsDef[]= {
+ {"filter", &mhmakefileparser::f_filter}
+ ,{"call", &mhmakefileparser::f_call}
+ ,{"subst", &mhmakefileparser::f_subst}
+ ,{"patsubst", &mhmakefileparser::f_patsubst}
+ ,{"concat", &mhmakefileparser::f_concat}
+ ,{"if", &mhmakefileparser::f_if}
+ ,{"findstring", &mhmakefileparser::f_findstring}
+ ,{"firstword", &mhmakefileparser::f_firstword}
+ ,{"wildcard", &mhmakefileparser::f_wildcard}
+ ,{"basename", &mhmakefileparser::f_basename}
+ ,{"notdir", &mhmakefileparser::f_notdir}
+ ,{"dir", &mhmakefileparser::f_dir}
+ ,{"shell", &mhmakefileparser::f_shell}
+ ,{"relpath", &mhmakefileparser::f_relpath}
+ ,{"toupper", &mhmakefileparser::f_toupper}
+ ,{"tolower", &mhmakefileparser::f_tolower}
+ ,{"exist", &mhmakefileparser::f_exist}
+ ,{"filesindirs",&mhmakefileparser::f_filesindirs}
+ ,{"fullname" ,&mhmakefileparser::f_fullname}
+ ,{"addprefix" ,&mhmakefileparser::f_addprefix}
+ ,{"addsuffix" ,&mhmakefileparser::f_addsuffix}
+ ,{"filter-out" ,&mhmakefileparser::f_filterout}
+ ,{"word" ,&mhmakefileparser::f_word}
+ ,{"words" ,&mhmakefileparser::f_words}
+ ,{"strip" ,&mhmakefileparser::f_strip}
+ ,{"which" ,&mhmakefileparser::f_which}
+ ,{"foreach" ,&mhmakefileparser::f_foreach}
+};
+
+map<string,function_f> mhmakefileparser::m_Functions;
+
+bool mhmakefileparser::m_FunctionsInitialised;
+
+///////////////////////////////////////////////////////////////////////////////
+// Loop over a list of filenames
+static string IterList(const string &List,string (*iterFunc)(const string &FileName,void *pArg), void *pArg=NULL)
+{
+ const char *pTmp=List.c_str();
+ string Ret=g_EmptyString;
+ bool first=true;
+ while (*pTmp)
+ {
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ Item=iterFunc(Item,pArg);
+ if (Item.empty())
+ first=true; // Do not add space the next iteration
+ else
+ Ret+=Item;
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::InitFuncs(void)
+{
+ for (int i=0; i<sizeof(m_FunctionsDef)/sizeof(funcdef); i++)
+ m_Functions[m_FunctionsDef[i].szFuncName]=m_FunctionsDef[i].pFunc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string TrimString(const string &Input)
+{
+ unsigned Start=0;
+ while (strchr(" \t",Input[Start])) Start++;
+ if (Start>=Input.size())
+ return g_EmptyString;
+ size_t Stop=Input.size()-1;
+ while (strchr(" \t",Input[Stop])) Stop--;
+ return Input.substr(Start,Stop-Start+1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static string filter(const string &FileName, void *pvFilter)
+{
+ string *pFilter=(string*)pvFilter;
+ if (PercentMatchList(UnquoteFileName(FileName),*pFilter))
+ return FileName;
+ else
+ return g_EmptyString;
+}
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filter(const string & Arg, const string *pOriExpr) const
+{
+ size_t ipos=Arg.find(',');
+ #ifdef _DEBUG
+ if (ipos==string::npos) {
+ throw string("filter func should have 2 arguments: ")+Arg;
+ }
+ #endif
+ string Str=TrimString(Arg.substr(0,ipos));
+ string List=Arg.substr(ipos+1);
+
+ if (Str.empty())
+ return Str;
+
+ return IterList(List,filter,(void*)&Str);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filterout(const string & Arg, const string *pOriExpr) const
+{
+ size_t ipos=Arg.find(',');
+ #ifdef _DEBUG
+ if (ipos==string::npos) {
+ throw string("filter func should have 2 arguments: ")+Arg;
+ }
+ #endif
+ string Str=TrimString(Arg.substr(0,ipos));
+ string List=Arg.substr(ipos+1);
+
+ if (Str.empty())
+ return Str;
+
+ bool First=true;
+ string Ret;
+ char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
+ while (pTok)
+ {
+ string Item(pTok);
+ if (!PercentMatchList(Item,Str))
+ {
+ if (First)
+ {
+ Ret=Item;
+ First=false;
+ }
+ else
+ {
+ Ret+=g_SpaceString;
+ Ret+=Item;
+ }
+ }
+ pTok=strtok(NULL," \t");
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_call(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ bool LastCharIsComma=Arg[Arg.length()-1]==',';
+
+ string Func;
+ pTmp=NextCharItem(pTmp,Func,',');
+ map<string,string>::const_iterator pFunc=m_Variables.find(Func);
+ #ifdef _DEBUG
+ if (pFunc==m_Variables.end())
+ {
+ throw string("call to non existing function ")+Func;
+ }
+ #endif
+ Func=pFunc->second;
+ int i=0;
+ while (*pTmp || LastCharIsComma) {
+ if (!*pTmp)
+ LastCharIsComma=false; /* To stop the loop */
+ string Repl;
+ pTmp=NextCharItem(pTmp,Repl,',');
+ i++;
+ char Tmp[10];
+ ::sprintf(Tmp,"$(%d)",i);
+ size_t Len=::strlen(Tmp);
+ size_t Pos=Func.find(Tmp);
+ while (Func.npos!=Pos)
+ {
+ Func=Func.substr(0,Pos)+Repl+Func.substr(Pos+Len);
+ Pos=Func.find(Tmp,Pos+Len);
+ }
+ }
+
+ return ExpandExpression(Func);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_subst(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string FromString;
+ pTmp=NextCharItem(pTmp,FromString,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ throw string("Wrong number of arguments in function subst");
+ }
+ #endif
+
+ string ToString;
+ pTmp=NextCharItem(pTmp,ToString,',');
+ string Text;
+ NextCharItem(pTmp,Text,',');
+
+ if (FromString.empty())
+ return Text;
+
+ string Ret;
+ size_t Pos=Text.find(FromString);
+ size_t PrevPos=0;
+ while (Pos!=string::npos)
+ {
+ Ret+=Text.substr(PrevPos,Pos-PrevPos);
+ Ret+=ToString;
+ PrevPos=Pos+FromString.length();
+ Pos=Text.find(FromString,PrevPos);
+ }
+ Ret+=Text.substr(PrevPos);
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_patsubst(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string FromString;
+ pTmp=NextCharItem(pTmp,FromString,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ throw string("Wrong number of arguments in function subst");
+ }
+ #endif
+
+ string ToString;
+ pTmp=NextCharItem(pTmp,ToString,',');
+ string Text;
+ NextCharItem(pTmp,Text,',');
+
+ if (FromString.empty())
+ return Text;
+
+ return Substitute(Text,FromString,ToString);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_concat(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string JoinString;
+ pTmp=NextCharItem(pTmp,JoinString,',');
+
+ string List;
+ pTmp=NextCharItem(pTmp,List,',');
+
+ if (JoinString.empty() && List.empty())
+ {
+ /* assume as $(concat ,,items) construct */
+ JoinString=",";
+ pTmp=NextCharItem(pTmp,List,',');
+ }
+
+ bool First=true;
+ string Ret;
+ char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
+ while (pTok)
+ {
+ if (First)
+ {
+ First=false;
+ }
+ else
+ {
+ Ret+=JoinString;
+ }
+ Ret+=pTok;
+ pTok=strtok(NULL," \t");
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_if(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string Cond;
+ pTmp=NextCharItem(pTmp,Cond,',');
+ string Ret;
+ if (Cond.empty())
+ {
+ pTmp=NextCharItem(pTmp,Ret,',');
+ }
+ NextCharItem(pTmp,Ret,',');
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_findstring(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+ string find;
+ pTmp=NextCharItem(pTmp,find,',');
+ string instr;
+ NextCharItem(pTmp,instr,',');
+
+ if (instr.find(find) != instr.npos)
+ return find;
+ else
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_firstword(const string & Arg, const string *pOriExpr) const
+{
+ string FirstWord;
+ NextItem(Arg.c_str(),FirstWord);
+ return FirstWord;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string wildcard(const string &Arg, void *pvVar)
+{
+ mhmakefileparser *pParser=(mhmakefileparser*)pvVar;
+
+ fileinfo *pFileSpec=GetFileInfo(Arg,pParser->GetMakeDir()); /* Use GetFileInfo to make the relative path absolute */
+ fileinfo *pDir=pFileSpec->GetDir();
+#ifdef WIN32
+ struct _finddata_t FileInfo;
+ intptr_t hFile=_findfirst(pFileSpec->GetFullFileName().c_str(),&FileInfo);
+ if (hFile==-1)
+ return g_EmptyString;
+
+ string Ret=g_EmptyString;
+
+ /* We have to verify with percentmatch since the find functions *.ext also matches the functions *.extbrol */
+ string CheckSpec=pFileSpec->GetName();
+ if (PercentMatchNoCase(FileInfo.name,CheckSpec,NULL,'*'))
+ {
+ Ret=GetFileInfo(FileInfo.name,pDir)->GetQuotedFullFileName();
+ }
+ while (-1!=_findnext(hFile,&FileInfo))
+ {
+ if (PercentMatchNoCase(FileInfo.name,CheckSpec,NULL,'*'))
+ {
+ Ret+=g_SpaceString;
+ Ret+=GetFileInfo(FileInfo.name,pDir)->GetQuotedFullFileName();
+ }
+ }
+ _findclose(hFile);
+#else
+ glob_t Res;
+ if (glob (pFileSpec->GetFullFileName().c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return g_EmptyString;
+
+ string Ret=g_EmptyString;
+ string SepStr=g_EmptyString;
+ string CheckSpec=pFileSpec->GetName();
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ if (PercentMatch(Res.gl_pathv[i],CheckSpec,NULL,'*'))
+ {
+ Ret+=SepStr;
+ Ret+=GetFileInfo(Res.gl_pathv[i],pDir)->GetQuotedFullFileName();
+ SepStr=g_SpaceString;
+ }
+ }
+
+ globfree(&Res);
+#endif
+ return Ret;
+}
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_wildcard(const string & Arg, const string *pOriExpr) const
+{
+ return IterList(Arg, wildcard, (void*)this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_exist(const string & Arg, const string *pOriExpr) const
+{
+ string File=TrimString(Arg);
+ fileinfo *pFile=GetFileInfo(File,m_MakeDir);
+ if (pFile->Exists())
+ {
+ return string("1");
+ }
+ else
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filesindirs(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string strFiles;
+ pTmp=NextCharItem(pTmp,strFiles,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ throw string("Wrong number of arguments in function filesindirs");
+ }
+ #endif
+ string strDirs;
+ NextCharItem(pTmp,strDirs,',');
+
+ vector<fileinfo*> Dirs;
+ SplitToItems(strDirs,Dirs);
+
+ pTmp=strFiles.c_str();
+ string Ret=g_EmptyString;
+ bool first=true;
+ while (*pTmp)
+ {
+ string File;
+ fileinfo *pFile;
+ pTmp=NextItem(pTmp,File);
+
+ vector<fileinfo*>::iterator It=Dirs.begin();
+ vector<fileinfo*>::iterator ItEnd=Dirs.end();
+ while (It!=ItEnd)
+ {
+ pFile=GetFileInfo(File,*It++);
+ if (pFile->Exists())
+ {
+ break;
+ }
+ pFile=NULL;
+ }
+ if (!pFile)
+ continue;
+
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ Ret+=pFile->GetQuotedFullFileName();
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_fullname(const string & Arg, const string *pOriExpr) const
+{
+ string File=TrimString(Arg);
+ fileinfo *pFile=GetFileInfo(File,m_MakeDir);
+ return pFile->GetQuotedFullFileName();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string basename(const string &FileName,void*)
+{
+ string Ret=UnquoteFileName(FileName);
+ Ret=Ret.substr(0,Ret.find_last_of('.'));
+ return QuoteFileName(Ret);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_basename(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,basename);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string notdir(const string &FileName,void*)
+{
+ string Ret=UnquoteFileName(FileName);
+ size_t Pos=Ret.find_last_of(OSPATHSEP);
+ if (Pos==string::npos)
+ {
+ return FileName;
+ }
+ else
+ {
+ Ret=Ret.substr(Pos+1);
+ return QuoteFileName(Ret);
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_notdir(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,notdir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string addprefix(const string &FileName,void *pPrefix)
+{
+ return *(const string *)pPrefix+FileName;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_addprefix(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+ string PreFix;
+ pTmp=NextCharItem(pTmp,PreFix,',');
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo && PreFix.empty()) {
+ cout << "Warning: empty prefix in expression: " << *pOriExpr << endl;
+ }
+ #endif
+ string FileNames;
+ pTmp=NextCharItem(pTmp,FileNames,',');
+ return IterList(FileNames,addprefix,&PreFix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string addsuffix(const string &FileName,void *pSuffix)
+{
+ return FileName+*(const string *)pSuffix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_addsuffix(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+ string SufFix;
+ pTmp=NextCharItem(pTmp,SufFix,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ throw string("Wrong number of arguments in function addsuffix");
+ }
+ #endif
+ string FileNames;
+ pTmp=NextCharItem(pTmp,FileNames,',');
+ return IterList(FileNames,addsuffix,&SufFix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns the n-th word number
+string mhmakefileparser::f_word(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string strNum;
+ pTmp=NextCharItem(pTmp,strNum,',');
+
+ int WordNbr=atoi(strNum.c_str());
+
+ #ifdef _DEBUG
+ if (!WordNbr)
+ {
+ if (!WordNbr) {
+ throw string ("Expecting a number bigger then 0 for the word function");
+ }
+ }
+ #endif
+
+ int CurWord=0;
+ while (*pTmp)
+ {
+ string Word;
+ pTmp=NextItem(pTmp,Word);
+ CurWord++;
+ if (CurWord==WordNbr)
+ return Word;
+ }
+
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns the number of words
+string mhmakefileparser::f_words(const string & Arg, const string *pOriExpr) const
+{
+ const char *pTmp=Arg.c_str();
+ int NrWords=0;
+ char szNumber[10];
+ while (*pTmp)
+ {
+ string Word;
+ pTmp=NextItem(pTmp,Word);
+ NrWords++;
+ }
+ sprintf(szNumber,"%d",NrWords);
+
+ return szNumber;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Search for a command in the enivornment path
+string mhmakefileparser::f_which(const string & Arg, const string *pOriExpr) const
+{
+ return SearchCommand(Arg);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Removes leading and trailing space
+string mhmakefileparser::f_strip(const string & Arg, const string *pOriExpr) const
+{
+ string::const_iterator pFirst=Arg.begin();
+ string::const_iterator pLast=Arg.end();
+ while (strchr(" \t\r\n",*pFirst) && pFirst!=pLast) pFirst++;
+ if (pFirst==pLast)
+ return "";
+ while (strchr(" \t\r\n",*(--pLast)));
+ pLast++;
+ return Arg.substr(pFirst-Arg.begin(),pLast-pFirst);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string dir(const string &FileName, void *)
+{
+ size_t Pos=FileName.find_last_of(OSPATHSEP);
+ if (Pos==string::npos)
+ {
+ return g_EmptyString;
+ }
+ else
+ {
+ string Ret=g_EmptyString;
+ Ret+=FileName.substr(0,Pos+1);
+ if (FileName[0]=='"' && FileName.end()[-1]=='"')
+ Ret+=s_QuoteString;
+ return Ret;
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_dir(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,dir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_shell(const string & Command, const string *pOriExpr) const
+{
+ string Output;
+
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "shell: executing: Command '"<<Command<<"'"<<endl;
+#endif
+
+ ((mhmakefileparser*)this)->ExecuteCommand(string("@")+Command,&Output); // Make sure that the command is not echoed
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "shell returned '"<<Output<<"'"<<endl;
+#endif
+ return Output;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string relpath(const string &FileName,void *pvDir)
+{
+ const fileinfo *pDir=*(const fileinfo **)pvDir;
+ const fileinfo *pPath=GetFileInfo(FileName,pDir);
+ const char *pCur=pDir->GetFullFileName().c_str();
+ const char *pszPath=pPath->GetFullFileName().c_str();
+
+ const char *pLast=pszPath;
+ while (*pCur==*pszPath)
+ {
+ char Char=*pszPath;
+ if (!Char)
+ {
+ return "."; // Means that FileName is the same as the current directory
+ }
+ if (Char==OSPATHSEP)
+ pLast=pszPath+1;
+ pCur++;
+ pszPath++;
+ }
+ if (*pszPath==OSPATHSEP && !*pCur)
+ pLast=pszPath+1;
+ string retPath;
+ if (*pCur==OSPATHSEP) {
+ bool first=true;
+ pCur++;
+ retPath="..";
+ while (*pCur)
+ {
+ if (*pCur==OSPATHSEP)
+ retPath+=OSPATHSEPSTR"..";
+ pCur++;
+ }
+ if (pszPath)
+ retPath=retPath+OSPATHSEPSTR+pLast;
+ }
+ else
+ {
+ if (*pCur)
+ retPath=".."OSPATHSEPSTR;
+ while (*pCur)
+ {
+ if (*pCur==OSPATHSEP)
+ retPath+=".."OSPATHSEPSTR;
+ pCur++;
+ }
+ retPath+=pLast;
+ }
+ return QuoteFileName(retPath);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Make a path name relative to the current directory
+string mhmakefileparser::f_relpath(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,relpath,(void*)&m_MakeDir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string makeupper(const string &FileName,void *)
+{
+ string Ret=FileName;
+ string::const_iterator pSrc=FileName.begin();
+ string::iterator pDest=Ret.begin();
+ while (pSrc!=FileName.end())
+ {
+ *pDest++ = toupper(*pSrc++);
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_toupper(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,makeupper);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string makelower(const string &FileName, void *)
+{
+ string Ret=FileName;
+ string::const_iterator pSrc=FileName.begin();
+ string::iterator pDest=Ret.begin();
+ while (pSrc!=FileName.end())
+ {
+ *pDest++ = tolower(*pSrc++);
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_tolower(const string & FileNames, const string *pOriExpr) const
+{
+ return IterList(FileNames,makelower);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+struct expanedexpr_arg
+{
+ string Var;
+ mhmakefileparser *pParser;
+ string Expr;
+};
+
+static string expandexpr(const string &VarVal, void *pvVar)
+{
+ expanedexpr_arg *pArg=(expanedexpr_arg*)pvVar;
+ pArg->pParser->SetVariable(pArg->Var,VarVal);
+ return pArg->pParser->ExpandExpression(pArg->Expr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_foreach(const string & ExpandedArg, const string *pOriExpr) const
+{
+#ifdef _DEBUG
+ if (pOriExpr->substr(0,8)!="foreach ")
+ throw(string("wrong assumption in foreach expression: ")+*pOriExpr);
+#endif
+ const char *pTmp=ExpandedArg.c_str();
+
+ expanedexpr_arg Args;
+ Args.pParser=(mhmakefileparser*)this;
+ pTmp=NextCharItem(pTmp,Args.Var,',');
+
+ if (Args.Var.empty())
+ throw(string("Wrong syntax in foreach instruction. Should give the variable a name."));
+
+ string Items;
+ pTmp=NextCharItem(pTmp,Items,',');
+
+ if (Items.empty())
+ return g_EmptyString; /* No items specified, so nothing needs to be done */
+
+ /* for the next item we need to use the original expression because we need to re-expand it using Var for each entry
+ in Items */
+ int Pos=pOriExpr->find_first_of(',',0);
+ if (Pos==string::npos)
+ throw(string("expected , in ")+*pOriExpr);
+ Pos=pOriExpr->find_first_of(',',Pos+1);
+ if (Pos==string::npos)
+ throw(string("expected two ,'s in ")+*pOriExpr);
+ Args.Expr=pOriExpr->substr(Pos+1);
+
+ /* Save the variable to be able to restore it after the foreach expansion */
+ string VarVal;
+ map<string,string>::const_iterator pVal=m_Variables.find(Args.Var);
+ if (pVal!=m_Variables.end())
+ VarVal=pVal->second;
+
+ string Ret=IterList(Items,expandexpr,(void*)&Args);
+
+ /* Restore the variable to it's original value */
+ if (pVal!=m_Variables.end())
+ Args.pParser->SetVariable(Args.Var,VarVal);
+ else
+ Args.pParser->DeleteVariable(Args.Var);
+
+ return Ret;
+}
+
diff --git a/tools/mhmake/src/md5.cpp b/tools/mhmake/src/md5.cpp
new file mode 100644
index 000000000..0ffba0d48
--- /dev/null
+++ b/tools/mhmake/src/md5.cpp
@@ -0,0 +1,455 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "stdafx.h"
+#include "md5.h"
+
+#ifdef _MSC_VER
+#pragma warning (disable:4005) /* macro redefinition */
+#endif
+
+#ifdef _DEBUG
+map<uint32,string> g_Md5Database;
+#endif
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32) (b)[(i) ] ) \
+ | ( (uint32) (b)[(i) + 1] << 8 ) \
+ | ( (uint32) (b)[(i) + 2] << 16 ) \
+ | ( (uint32) (b)[(i) + 3] << 24 ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8) ( (n) ); \
+ (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
+ (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
+ (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \
+}
+
+void md5_starts( md5_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+#ifdef _DEBUG
+ if (g_BuildMd5Db) ctx->Data="";
+#endif
+}
+
+void md5_process( md5_context *ctx, uint8 data[64] )
+{
+ uint32 X[16], A, B, C, D;
+
+ GET_UINT32( X[0], data, 0 );
+ GET_UINT32( X[1], data, 4 );
+ GET_UINT32( X[2], data, 8 );
+ GET_UINT32( X[3], data, 12 );
+ GET_UINT32( X[4], data, 16 );
+ GET_UINT32( X[5], data, 20 );
+ GET_UINT32( X[6], data, 24 );
+ GET_UINT32( X[7], data, 28 );
+ GET_UINT32( X[8], data, 32 );
+ GET_UINT32( X[9], data, 36 );
+ GET_UINT32( X[10], data, 40 );
+ GET_UINT32( X[11], data, 44 );
+ GET_UINT32( X[12], data, 48 );
+ GET_UINT32( X[13], data, 52 );
+ GET_UINT32( X[14], data, 56 );
+ GET_UINT32( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) \
+{ \
+ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P( A, B, C, D, 0, 7, 0xD76AA478 );
+ P( D, A, B, C, 1, 12, 0xE8C7B756 );
+ P( C, D, A, B, 2, 17, 0x242070DB );
+ P( B, C, D, A, 3, 22, 0xC1BDCEEE );
+ P( A, B, C, D, 4, 7, 0xF57C0FAF );
+ P( D, A, B, C, 5, 12, 0x4787C62A );
+ P( C, D, A, B, 6, 17, 0xA8304613 );
+ P( B, C, D, A, 7, 22, 0xFD469501 );
+ P( A, B, C, D, 8, 7, 0x698098D8 );
+ P( D, A, B, C, 9, 12, 0x8B44F7AF );
+ P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+ P( B, C, D, A, 11, 22, 0x895CD7BE );
+ P( A, B, C, D, 12, 7, 0x6B901122 );
+ P( D, A, B, C, 13, 12, 0xFD987193 );
+ P( C, D, A, B, 14, 17, 0xA679438E );
+ P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P( A, B, C, D, 1, 5, 0xF61E2562 );
+ P( D, A, B, C, 6, 9, 0xC040B340 );
+ P( C, D, A, B, 11, 14, 0x265E5A51 );
+ P( B, C, D, A, 0, 20, 0xE9B6C7AA );
+ P( A, B, C, D, 5, 5, 0xD62F105D );
+ P( D, A, B, C, 10, 9, 0x02441453 );
+ P( C, D, A, B, 15, 14, 0xD8A1E681 );
+ P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
+ P( A, B, C, D, 9, 5, 0x21E1CDE6 );
+ P( D, A, B, C, 14, 9, 0xC33707D6 );
+ P( C, D, A, B, 3, 14, 0xF4D50D87 );
+ P( B, C, D, A, 8, 20, 0x455A14ED );
+ P( A, B, C, D, 13, 5, 0xA9E3E905 );
+ P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
+ P( C, D, A, B, 7, 14, 0x676F02D9 );
+ P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+
+ P( A, B, C, D, 5, 4, 0xFFFA3942 );
+ P( D, A, B, C, 8, 11, 0x8771F681 );
+ P( C, D, A, B, 11, 16, 0x6D9D6122 );
+ P( B, C, D, A, 14, 23, 0xFDE5380C );
+ P( A, B, C, D, 1, 4, 0xA4BEEA44 );
+ P( D, A, B, C, 4, 11, 0x4BDECFA9 );
+ P( C, D, A, B, 7, 16, 0xF6BB4B60 );
+ P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+ P( A, B, C, D, 13, 4, 0x289B7EC6 );
+ P( D, A, B, C, 0, 11, 0xEAA127FA );
+ P( C, D, A, B, 3, 16, 0xD4EF3085 );
+ P( B, C, D, A, 6, 23, 0x04881D05 );
+ P( A, B, C, D, 9, 4, 0xD9D4D039 );
+ P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+ P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+ P( B, C, D, A, 2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P( A, B, C, D, 0, 6, 0xF4292244 );
+ P( D, A, B, C, 7, 10, 0x432AFF97 );
+ P( C, D, A, B, 14, 15, 0xAB9423A7 );
+ P( B, C, D, A, 5, 21, 0xFC93A039 );
+ P( A, B, C, D, 12, 6, 0x655B59C3 );
+ P( D, A, B, C, 3, 10, 0x8F0CCC92 );
+ P( C, D, A, B, 10, 15, 0xFFEFF47D );
+ P( B, C, D, A, 1, 21, 0x85845DD1 );
+ P( A, B, C, D, 8, 6, 0x6FA87E4F );
+ P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+ P( C, D, A, B, 6, 15, 0xA3014314 );
+ P( B, C, D, A, 13, 21, 0x4E0811A1 );
+ P( A, B, C, D, 4, 6, 0xF7537E82 );
+ P( D, A, B, C, 11, 10, 0xBD3AF235 );
+ P( C, D, A, B, 2, 15, 0x2AD7D2BB );
+ P( B, C, D, A, 9, 21, 0xEB86D391 );
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+static uint8 md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void md5_update( md5_context *ctx, uint8 *input, size_t length )
+{
+ uint32 left, fill;
+
+ if( ! length ) return;
+
+#ifdef _DEBUG
+ if (g_BuildMd5Db && md5_padding!=input && ctx->msglen!=input)
+ ctx->Data+=string((char*)input,length);
+#endif
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] = (uint32)((ctx->total[0] + length)& 0xFFFFFFFF);
+
+ if( ctx->total[0] < length )
+ ctx->total[1]++;
+
+ if( left && length >= fill )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, fill );
+ md5_process( ctx, ctx->buffer );
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while( length >= 64 )
+ {
+ md5_process( ctx, input );
+ length -= 64;
+ input += 64;
+ }
+
+ if( length )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, length );
+ }
+}
+
+uint32 *md5_finishbin( md5_context *ctx)
+{
+ uint32 last, padn;
+ uint32 high, low;
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_UINT32( low, ctx->msglen, 0 );
+ PUT_UINT32( high, ctx->msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ md5_update( ctx, md5_padding, padn );
+ md5_update( ctx, ctx->msglen, 8 );
+
+ return ctx->state;
+}
+
+void md5_finish( md5_context *ctx, uint8 digest[16] )
+{
+ md5_finishbin( ctx);
+ PUT_UINT32( ctx->state[0], digest, 0 );
+ PUT_UINT32( ctx->state[1], digest, 4 );
+ PUT_UINT32( ctx->state[2], digest, 8 );
+ PUT_UINT32( ctx->state[3], digest, 12 );
+}
+
+uint32 md5_finish32( md5_context *ctx)
+{
+ md5_finishbin( ctx);
+ uint32 Md5_32=ctx->state[0]+ctx->state[1]+ctx->state[2]+ctx->state[3];
+#ifdef _DEBUG
+ if (g_BuildMd5Db) g_Md5Database[Md5_32]=ctx->Data;
+#endif
+ return Md5_32;
+}
+
+#ifdef _DEBUG
+struct WRITEMD5DB
+{
+ ~WRITEMD5DB()
+ {
+ if (g_BuildMd5Db)
+ {
+ FILE *pFile=fopen("Md5.database","wb");
+ map<uint32,string>::const_iterator It=g_Md5Database.begin();
+ while (It!=g_Md5Database.end())
+ {
+ fprintf(pFile,"%08x: ",It->first);
+ fwrite(It->second.c_str(),It->second.length(),1,pFile);
+ fprintf(pFile,"\n");
+ It++;
+ }
+ fclose(pFile);
+ }
+ }
+};
+static WRITEMD5DB WriteMd5Db;
+#endif
+
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * those are the standard RFC 1321 test vectors
+ */
+
+static char *msg[] =
+{
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012" \
+ "345678901234567890"
+};
+
+static char *val[] =
+{
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "f96b697d7cb7938d525a2f31aaf161d0",
+ "c3fcd3d76192e4007dfb496cca67e13b",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "57edf4a22be3c955ac49da2e2107b67a"
+};
+
+int main( int argc, char *argv[] )
+{
+ FILE *f;
+ int i, j;
+ char output[33];
+ md5_context ctx;
+ unsigned char buf[1000];
+ unsigned char md5sum[16];
+
+ if( argc < 2 )
+ {
+ printf( "\n MD5 Validation Tests:\n\n" );
+
+ for( i = 0; i < 7; i++ )
+ {
+ printf( " Test %d ", i + 1 );
+
+ md5_starts( &ctx );
+ md5_update( &ctx, (uint8 *) msg[i], strlen( msg[i] ) );
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ sprintf( output + j * 2, "%02x", md5sum[j] );
+ }
+
+ if( memcmp( output, val[i], 32 ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
+
+ printf( "\n" );
+ }
+ else
+ {
+ if( ! ( f = fopen( argv[1], "rb" ) ) )
+ {
+ perror( "fopen" );
+ return( 1 );
+ }
+
+ md5_starts( &ctx );
+
+ while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
+ {
+ md5_update( &ctx, buf, i );
+ }
+
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ printf( "%02x", md5sum[j] );
+ }
+
+ printf( " %s\n", argv[1] );
+ }
+
+ return( 0 );
+}
+
+#endif
+
+//#define APP
+#ifdef APP
+#include <stdio.h>
+
+int main( int argc, char *argv[] )
+{
+ unsigned j;
+ unsigned char md5sum[16];
+ md5_context ctx;
+ FILE *pFile=fopen(argv[1],"r");
+
+ if (!pFile)
+ {
+ printf("Error opening file.\n");
+ return 1;
+ }
+
+ md5_starts( &ctx );
+
+ while (1)
+ {
+ char Buf[1024];
+ size_t Ret=fread(Buf,1,sizeof(Buf),pFile);
+ if (!Ret)
+ break;
+ md5_update( &ctx, (uint8 *) Buf, Ret);
+ }
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ printf("%02x", md5sum[j] );
+ }
+
+}
+#endif
+
diff --git a/tools/mhmake/src/md5.h b/tools/mhmake/src/md5.h
new file mode 100644
index 000000000..f16c9bd19
--- /dev/null
+++ b/tools/mhmake/src/md5.h
@@ -0,0 +1,58 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+ uint32 total[2];
+ uint32 state[4];
+ uint8 buffer[64];
+ uint8 msglen[8];
+#ifdef _DEBUG
+ string Data;
+#endif
+}
+md5_context;
+
+typedef uint32 md5_val[4];
+
+void md5_starts( md5_context *ctx );
+void md5_update( md5_context *ctx, uint8 *input, size_t length );
+void md5_finish( md5_context *ctx, uint8 digest[16] );
+uint32 *md5_finishbin( md5_context *ctx);
+uint32 md5_finish32( md5_context *ctx);
+
+#ifdef _DEBUG
+extern bool g_BuildMd5Db;
+#endif
+
+#endif /* md5.h */
+
+
diff --git a/tools/mhmake/src/mhmake.cpp b/tools/mhmake/src/mhmake.cpp
new file mode 100644
index 000000000..6d99e5f3d
--- /dev/null
+++ b/tools/mhmake/src/mhmake.cpp
@@ -0,0 +1,158 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+#include "fileinfo.h"
+#include "mhmakeparser.h"
+#include "rule.h"
+#include "util.h"
+
+bool g_Clean=false;
+bool g_StopCompiling=false;
+
+#ifdef WIN32
+BOOL WINAPI ControlCHandler(DWORD dwCtrlType)
+{
+ g_StopCompiling=true;
+ return TRUE;
+}
+#endif
+
+int __CDECL main(int argc, char* argv[])
+{
+ //__int64 Freq;
+ //__int64 Start;
+
+ //QueryPerformanceFrequency((LARGE_INTEGER*)&Freq);
+ //QueryPerformanceCounter((LARGE_INTEGER*)&Start);
+
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
+
+ // Turn on leak-checking bit
+ tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
+ // Turn on defer freeing
+ //tmpFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
+ // Turn on heap checking
+ //tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
+
+ // Set flag to the new value
+ _CrtSetDbgFlag( tmpFlag );
+
+ //_CrtSetBreakAlloc(44);
+ #endif
+
+ #ifdef WIN32
+ /* Remove the VS_UNICODE_OUTPUT environment variable. This variable is set when running from
+ * the Visual Studio IDE and is causing the output of cl.exe to send the output directly to the IDE instead
+ * of sending it to stdout. This is causing all scripts that are calling cl.exe and intercept the
+ * output to fail.
+ */
+ putenv("VS_UNICODE_OUTPUT=");
+
+ SetConsoleCtrlHandler(ControlCHandler, TRUE);
+
+ #endif
+
+ try
+ {
+ mhmakefileparser::InitBuildTime();
+
+ char PlatformEnv[]="PLATFORM="PLATFORM;
+ putenv(PlatformEnv);
+
+ vector<string> CmdLineArgs;
+ for (int i=1; i<argc; i++)
+ {
+ CmdLineArgs.push_back(argv[i]);
+ }
+
+ refptr<loadedmakefile> pFirstMakefile(new loadedmakefile(curdir::GetCurDir(),CmdLineArgs,"makefile"));
+ // For the first makefile we add the defines passed on the command line to the
+ // environment so that the other load_makefile see the same variables
+ pFirstMakefile->AddCommandLineVarsToEnvironment();
+
+ g_LoadedMakefiles.push_back(pFirstMakefile);
+
+ pFirstMakefile->LoadMakefile();
+
+ #ifdef _DEBUG
+ if (g_PrintVarsAndRules)
+ {
+ DumpVarsAndRules();
+ }
+ #endif
+
+ // Make sure that the included makefiles that have rules are build
+
+ LOADEDMAKEFILES::iterator LoadMakIt=g_LoadedMakefiles.begin();
+ while (LoadMakIt!=g_LoadedMakefiles.end())
+ {
+ (*LoadMakIt)->m_pParser->BuildIncludedMakefiles();
+ (*LoadMakIt)->m_pParser->ParseBuildedIncludeFiles();
+ (*LoadMakIt)->m_pParser->CheckEnv();
+ LoadMakIt++;
+ }
+
+ if (pFirstMakefile->m_CommandLineTargets.size())
+ {
+ vector<string>::iterator It=pFirstMakefile->m_CommandLineTargets.begin();
+ while (It!=pFirstMakefile->m_CommandLineTargets.end())
+ {
+ if (*It=="clean" || *It=="cleanall")
+ {
+ g_Clean=true;
+ }
+ fileinfo *pTarget=GetFileInfo(*It,pFirstMakefile->m_MakeDir);
+ pFirstMakefile->m_pParser->BuildTarget(pTarget);
+ It++;
+ }
+ }
+ else
+ {
+ fileinfo *pFirstTarget=pFirstMakefile->m_pParser->GetFirstTarget();
+ if (pFirstTarget)
+ {
+ pFirstMakefile->m_pParser->BuildTarget(pFirstTarget);
+ }
+ else
+ cout << "Warning: no targets in makefile. Nothing to be build.\nMHMAKECONF defined?\n";
+ }
+
+ }
+ catch (string Message)
+ {
+ cerr << "Error occured: " << Message << endl;
+ #ifdef _DEBUG
+ if (g_DumpOnError)
+ {
+ DumpVarsAndRules();
+ }
+ #endif
+ return 1;
+ }
+
+ //__int64 Stop;
+ //QueryPerformanceCounter((LARGE_INTEGER*)&Stop);
+ //cout << (Stop-Start)*1000/Freq << endl;
+
+ return 0;
+}
+
diff --git a/tools/mhmake/src/mhmakefileparser.cpp b/tools/mhmake/src/mhmakefileparser.cpp
new file mode 100644
index 000000000..f281bc7dd
--- /dev/null
+++ b/tools/mhmake/src/mhmakefileparser.cpp
@@ -0,0 +1,1451 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+#include "util.h"
+#include "md5.h"
+
+#include "mhmakefileparser.h"
+#include "rule.h"
+#include "flexlexer.h"
+
+commandqueue mhmakefileparser::sm_CommandQueue;
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::yylex(void)
+{
+ m_yyloc=m_ptheLexer->lineno();
+ return m_ptheLexer->yylex(m_theTokenValue);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::yyerror(const char *m)
+{
+ cerr << this->m_ptheLexer->m_InputFileName<< " ("<<m_yyloc<<"): "<<m<<endl;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::ParseFile(const fileinfo *pFileInfo, const fileinfo *pMakeDir)
+{
+ if (pMakeDir)
+ {
+ m_MakeDir=pMakeDir;
+ m_Variables[CURDIR]=m_MakeDir->GetQuotedFullFileName();
+ }
+
+ ifstream yyin(pFileInfo->GetFullFileName().c_str(),ios_base::in);
+ if (yyin.fail())
+ {
+ cerr << "Error opening makefile: "<<pFileInfo->GetQuotedFullFileName()<<endl;
+ return 1;
+ }
+
+ mhmakeFlexLexer theLexer(&yyin);
+ m_ptheLexer=&theLexer;
+ theLexer.m_InputFileName=pFileInfo->GetFullFileName();
+ theLexer.m_pParser=(mhmakeparser*)this;
+ int Ret=yyparse();
+ return Ret;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsDefined(const string &Var) const
+{
+ bool Ret = m_Variables.find(Var)!=m_Variables.end();
+ if (!Ret)
+ {
+ string Env=GetFromEnv(Var);
+ if (!Env.empty())
+ {
+ Ret=true;
+ }
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline size_t SkipUntilQuote(const string &Expr,size_t i,char Char)
+{
+ while (Expr[i++]!=Char) ;
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline size_t SkipMakeExpr(const string &Expr,size_t i)
+{
+ char Char=Expr[i++];
+ if (Char!='(')
+ return i;
+ Char=Expr[i++];
+ while (Char!=')')
+ {
+ if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+#ifdef _DEBUG
+ if (i>=Expr.length())
+ throw(string(") not found in ")+Expr);
+#endif
+ Char=Expr[i++];
+ }
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Splits expression on the Item, but the item may not occur within
+// a macro or quoted string
+static pair<string,string> SplitExpr(const string &Expr,char Item)
+{
+ size_t i=0;
+ char Char=Expr[i++];
+ while (Char!=Item)
+ {
+ if (Char=='"' || Char=='\'')
+ {
+ i=SkipUntilQuote(Expr,i,Char);
+ }
+ else if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+ Char=Expr[i++];
+ }
+ return pair<string,string>(Expr.substr(0,i-1),Expr.substr(i));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsEqual(const string &EqualExpr) const
+{
+ string Expr=ExpandExpression(EqualExpr);
+ const char *pStr=Expr.c_str();
+ const char *pTmp=pStr;
+ while (*pTmp && *pTmp!=',') pTmp++;
+ ptrdiff_t Pos=pTmp-pStr;
+ size_t Size=Expr.size();
+ pTmp=pStr+Size-1;
+ while (pTmp>pStr && strchr(" \t",*pTmp))
+ {
+ pTmp--;
+ }
+ if (2*Pos != pTmp-pStr)
+ {
+ return false;
+ }
+ pTmp=pStr;
+ const char *pTmp2=pTmp+Pos+1;
+ if (*pTmp=='(')
+ {
+ pTmp++;
+ Pos--;
+ }
+ for (int i=0; i<Pos; i++)
+ {
+ if (pTmp[i]!=pTmp2[i])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandExpression(const string &Expr) const
+{
+ bool Recurse;
+ string Ret(Expr);
+ do
+ {
+ Recurse=false;
+ Ret=ExpandExpressionRecurse(Ret,Recurse);
+ }
+ while (Recurse);
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandExpressionRecurse(const string &Expr, bool &Recurse) const
+{
+ //((mhmakefileparser*)this)->m_InExpandExpression++;
+ size_t i=0;
+ size_t Length=Expr.size();
+ string Ret;
+ string ToAdd;
+ while (i<Length)
+ {
+ char Char=Expr[i++];
+ if (Char=='$')
+ {
+ char CharNext=Expr[i];
+ if (CharNext=='$')
+ {
+ ToAdd="$";
+ i++;
+ }
+ else
+ {
+ size_t inew=SkipMakeExpr(Expr,i);
+ i++;
+ if (inew>i)
+ {
+ ToAdd=ExpandMacro(Expr.substr(i,inew-i-1),Recurse);
+ i=inew;
+ }
+ else
+ {
+ // This is a single character expression
+ ToAdd=ExpandMacro(string(1,Expr[i-1]),Recurse);
+ }
+ }
+ Ret+=ToAdd;
+ }
+ else
+ {
+ Ret+=Char;
+ }
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandMacro(const string &Expr, bool &Recurse) const
+{
+ string ExpandedExpr=ExpandExpressionRecurse(Expr,Recurse);
+
+ const char *pTmp=ExpandedExpr.c_str();
+ /* First remove leading spaces */
+ while (*pTmp==' ' || *pTmp=='\t') pTmp++;
+ const char *pVar=pTmp;
+ while (*pTmp && *pTmp!=' ' && *pTmp!='\t' && *pTmp!=':') pTmp++;
+ const char *pVarEnd=pTmp;
+ char Type=*pTmp++;
+ while (*pTmp && (*pTmp==' ' || *pTmp=='\t')) pTmp++;
+ if (Type&&*pTmp)
+ { // We have a match for the regular expression ^([^ \\t:]+)([: \\t])[ \\t]*(.+)
+ if (Type==':')
+ {
+ #ifdef WIN32
+ bool IsFileName=false;
+ if (pVarEnd-pVar == 1 && (*pVar=='<' || *pVar =='@'))
+ IsFileName=true;
+ #endif
+ string ToSubst=ExpandExpression(ExpandVar(string(pVar,pVarEnd)));
+ const char *pSrc=pTmp;
+ const char *pStop=pSrc;
+ while (*pStop!='=') pStop++;
+ const char *pTo=pStop+1;
+ string SrcStr(pSrc,pStop);
+ string ToStr(pTo);
+ #ifdef WIN32
+ if (IsFileName)
+ {
+ matchres Res;
+ string FileName(UnquoteFileName(ToSubst));
+ if (PercentMatch(FileName,UnquoteFileName(SrcStr),&Res))
+ {
+ FileName=ReplaceWithStem(UnquoteFileName(ToStr),Res.m_Stem);
+ }
+ return QuoteFileName(FileName);
+ }
+ #endif
+ return Substitute(ToSubst,SrcStr,ToStr);
+ }
+ else if (Type==' ' || Type == '\t')
+ {
+ string Func(pVar,pVarEnd);
+ string Arg(pTmp);
+ if (Recurse)
+ {
+ #ifdef _DEBUG
+ if (!(Arg.find('%')!=string::npos && Arg.find('$')!=string::npos))
+ throw(string("Bug in mhmake: expected a % and $ sign: ")+Arg);
+ #endif
+ return string("$(")+ExpandedExpr+")"; // we cannot call the function yet since there is still a $(*%*) macro to resolve. so
+ // return it a a macro again so it can be resolved when the % is resolved
+ // remark that the current test is not completely safe because the % could be out of
+ // the $ macro
+ }
+ function_f pFunc=m_Functions[Func];
+ #ifdef _DEBUG
+ if (pFunc)
+ {
+ return (this->*pFunc)(Arg, &Expr);
+ }
+ else
+ {
+ throw string("Unknown function specified in macro: ")+Func;
+ }
+ #else
+ return (this->*pFunc)(Arg, &Expr);
+ #endif
+ }
+ else
+ {
+ #ifdef _DEBUG
+ throw string("Fatal error in ExpandMacro (bug in mhmake ? ? ?)");
+ #else
+ return g_EmptyString;
+ #endif
+ }
+ }
+ else
+ {
+ if (ExpandedExpr.find('%')!=string::npos)
+ {
+ // we have encountered a *%* macro. This means a previous subst is not yet finished. so return
+ // it back as a macro so it can be expanded again later when the % is replaced
+ Recurse=true;
+ return string("$(")+ExpandedExpr+")";
+ }
+ #ifdef _DEBUG
+ else if (ExpandedExpr.find('$')!=string::npos)
+ throw(string("Bug in mhmake: wasn't expecting a $ sign in: ")+ExpandedExpr);
+ #endif
+ else
+ {
+ return ExpandExpression(ExpandVar(ExpandedExpr));
+ }
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandVar(const string &Var) const
+{
+ map<string,string>::const_iterator pIt=m_Variables.find(Var);
+ if (pIt==m_Variables.end())
+ {
+ if (Var.size()==1)
+ {
+ char Char=Var[0];
+ if (m_RuleThatIsBuild)
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+#ifdef _DEBUG
+ if (!m_RuleThatIsBuild->GetDeps().size())
+ {
+ return "<No Dependencies defined.>";
+ }
+#endif
+ return m_RuleThatIsBuild->GetDeps()[0]->GetQuotedFullFileName();
+ case '@': // return full target file name
+ return m_RuleThatIsBuild->GetQuotedFullFileName();
+ case '*': // return stem
+ return m_RuleThatIsBuild->GetRule()->GetStem();
+ case '^': // return all prerequisits
+ return m_RuleThatIsBuild->GetPrerequisits();
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+ case '@': // return full target file name
+ case '*': // return stem
+ case '^': // return all prerequisits
+ return Var; // To make comparing of rules more accurate
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ }
+ string Env=GetFromEnv(Var);
+ if (Env.empty())
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Warning: Variable "<<Var<<" not found\n";
+ }
+ #endif
+ return g_EmptyString;
+ }
+ else
+ return Env;
+ }
+ else
+ return pIt->second;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::SplitToItems(const string &String,vector<fileinfo*> &Items) const
+{
+ const char *pTmp=String.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (!Item.empty())
+ {
+ Items.push_back(GetFileInfo(Item,m_MakeDir));
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::ParseBuildedIncludeFiles()
+{
+ vector<string>::iterator It=m_ToBeIncludeAfterBuild.begin();
+ while (It!=m_ToBeIncludeAfterBuild.end())
+ {
+ int result=ParseFile(GetFileInfo(*It,m_MakeDir));
+ if (result)
+ {
+ throw string("Error parsing ")+*It;
+ }
+ It++;
+ }
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::PrintVariables(bool Expand) const
+{
+ map<string,string>::const_iterator It=m_Variables.begin();
+ while (It!=m_Variables.end())
+ {
+ if (Expand)
+ {
+ try
+ {
+ cout<<It->first<<" : "<<ExpandExpression(It->second)<<endl;
+ }
+ catch (...)
+ {
+ cout<<endl;
+ }
+ }
+ else
+ {
+ cout<<It->first<<" : "<<It->second<<endl;
+ }
+ It++;
+ }
+ if (m_pEnv)
+ {
+ cout << "Environment overruled:\n";
+#ifdef WIN32
+ char *pEnv=m_pEnv;
+ while (*pEnv)
+ {
+ cout<<pEnv<<endl;
+ pEnv+=strlen(pEnv)+1;
+ }
+#else
+ char **pEnv=m_pEnv;
+ while (*pEnv)
+ {
+ cout<<*pEnv<<endl;
+ pEnv++;
+ }
+#endif
+ }
+}
+#endif
+
+//#define PAGETOSTRING(Nr) #Nr
+//#define PAGETONBR(Nr) PAGETOSTRING(##Nr)
+//#pragma message("ar=" PAGETONBR(NULL) ";")
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::AddRule()
+{
+ fileinfoarray::iterator pIt=m_pCurrentItems->begin();
+ while (pIt!=m_pCurrentItems->end())
+ {
+ if ((*pIt)->GetFullFileName().find('%')!=string::npos)
+ {
+ IMPLICITRULE::AddImplicitRule(*pIt,*m_pCurrentDeps,m_pCurrentRule);
+ }
+ else
+ {
+ // If we had a double colon we must make sure that the target is always build
+ if (m_DoubleColonRule)
+ {
+ (*pIt)->SetNotExist();
+ }
+ (*pIt)->SetRuleIfNotExist(m_pCurrentRule);
+ if (!m_pCurrentRule)
+ (*pIt)->AddDeps(*m_pCurrentDeps);
+ else
+ (*pIt)->InsertDeps(*m_pCurrentDeps);
+ if (!m_FirstTarget)
+ { // Only check this if the rule is not an implicit rule
+ m_FirstTarget=(*m_pCurrentItems)[0];
+ }
+ }
+ pIt++;
+ }
+ m_pCurrentItems=NULL;
+ m_pCurrentRule=NullRule;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::GetAutoDeps(const fileinfo *pFirstDep, deps_t &Autodeps)
+{
+ /* Here we have to scan only c/c++ headers so skip certain extensions */
+ const char *pFullName=pFirstDep->GetFullFileName().c_str();
+ const char *pExt=strrchr(pFullName,'.');
+ bool bPython=false;
+ if (pExt)
+ {
+ if (!_stricmp(pExt+1,"py"))
+ bPython=true;
+ }
+
+ FILE *pIn=fopen(pFullName,"r");
+ if (!pIn)
+ return;
+
+ char IncludeList[255];
+ int PrevRet=0;
+ int Ret=fgetc(pIn);
+ while(Ret!=EOF)
+ {
+ char Type[2];
+ bool bFound=false;
+ if (bPython)
+ {
+ Type[0]='"';
+ if (Ret=='i')
+ {
+ Ret=fscanf(pIn,"mport %254[^\"\n]",IncludeList);
+ if (Ret==1)
+ {
+ if (IncludeList[0]!='*')
+ bFound=true;
+ }
+ }
+ }
+ else
+ {
+ if (PrevRet=='/')
+ {
+ if (Ret=='/')
+ {
+ /* This is a C++ command so read until the next line-feed */
+ do
+ {
+ Ret=fgetc(pIn);
+ } while (Ret!='\n' && Ret!=EOF);
+ }
+ else if (Ret=='*')
+ {
+ /* This is a standard C comment, so read until then end of the command */
+ do
+ {
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ } while ((PrevRet!='*' || Ret!='/') && Ret!=EOF);
+ }
+ }
+ else if (Ret=='#' || Ret=='.')
+ {
+ if (Ret=='#')
+ {
+ fscanf(pIn,"%*[ \t]");
+ Ret=fscanf(pIn,"include %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ }
+ else
+ Ret=fscanf(pIn,"import %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ if (Ret==2)
+ {
+ bFound=true;
+ }
+ }
+ }
+ if (bFound)
+ {
+ const char *pTmp=IncludeList;
+ while (*pTmp)
+ {
+ string IncludeFile;
+ pTmp=NextItem(pTmp,IncludeFile," \t,");
+ if (bPython)
+ IncludeFile+=".py";
+
+ if (SkipHeaderFile(IncludeFile))
+ continue;
+ #ifdef _DEBUG
+ m_ImplicitSearch++; // This is to avoid warnings of targets that does not exist
+ #endif
+ if (Type[0]=='"')
+ {
+ fileinfo *pInclude=GetFileInfo(IncludeFile,pFirstDep->GetDir());
+ /* Add the dependency when the file alrady exist or there is a rule available to be build */
+ mh_time_t Date=BuildTarget(pInclude);
+ if (Date.DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ deps_t::const_iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDepsIfNeeded(pInclude,pInclude);
+ }
+ }
+ }
+ const refptr<fileinfoarray> IncludeDirs=GetIncludeDirs();
+ fileinfoarray::const_iterator It=IncludeDirs->begin();
+ while (It<IncludeDirs->end())
+ {
+ fileinfo *pInclude=GetFileInfo(IncludeFile,*It);
+ mh_time_t Date=BuildTarget(pInclude);
+ if (Date.DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ deps_t::const_iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDepsIfNeeded(pInclude,pInclude);
+ }
+ break;
+ }
+ It++;
+ }
+ #ifdef _DEBUG
+ m_ImplicitSearch--;
+ #endif
+ }
+ }
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ }
+ fclose(pIn);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void mhmakefileparser::GetAutoDepsIfNeeded(fileinfo *pTarget, const fileinfo *pFirstDep)
+{
+ autodeps_entry_t &Autodeps=m_AutoDeps[pTarget];
+ if (!Autodeps.first)
+ {
+ Autodeps.first=true;
+ /* We are going to rescan, so throw away the old. */
+ Autodeps.second.clear();
+ GetAutoDeps(pFirstDep,Autodeps.second);
+ // Now add these dependencies also to the rules
+ deps_t::iterator It=Autodeps.second.begin();
+ while (It!=Autodeps.second.end())
+ {
+ pTarget->AddDep(*It);
+ It++;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void mhmakefileparser::UpdateAutomaticDependencies(fileinfo *pTarget)
+{
+ if (pTarget->IsAutoDepExtention())
+ {
+ // we have to search for the include files in the first dependency of Target
+ vector<fileinfo*> &Deps=pTarget->GetDeps();
+ if (!Deps.size())
+ return; // There is no first dep
+ fileinfo *pFirstDep=Deps[0];
+ GetAutoDepsIfNeeded(pTarget,pFirstDep);
+ SetAutoDepsDirty();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::UpdateNoRuleAutomaticDependencies(fileinfo *pTarget)
+{
+ // we have to search for the include files in the Target
+ deps_t Autodeps;
+ GetAutoDeps(pTarget,Autodeps);
+ // Now add these dependencies also to the rules
+ deps_t::iterator It=Autodeps.begin();
+ while (It!=Autodeps.end())
+ {
+ pTarget->AddDep(*It);
+ It++;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+const refptr<fileinfoarray> mhmakefileparser::GetIncludeDirs() const
+{
+ string Includes=ExpandExpression("$(INCLUDES)");
+ if (!m_pIncludeDirs || Includes != m_IncludeDirs)
+ {
+ ((mhmakefileparser*)this)->m_IncludeDirs=Includes;
+ ((mhmakefileparser*)this)->m_pIncludeDirs=refptr<fileinfoarray>(new fileinfoarray);
+ if (Includes.empty()) // If not defined try a default path
+ Includes="include inc .."OSPATHSEPSTR"include .."OSPATHSEPSTR"inc";
+ const char *pTmp=Includes.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ fileinfo *pIncDir=GetFileInfo(Item,m_MakeDir);
+ if (pIncDir->Exists() || pIncDir->GetRule())
+ ((mhmakefileparser*)this)->m_pIncludeDirs->push_back(pIncDir);
+ }
+ }
+ return m_pIncludeDirs;
+}
+
+static void ReadStr(FILE *pFile,char *Str)
+{
+ int i=0;
+ while (1)
+ {
+ Str[i]=fgetc(pFile);
+#ifdef _DEBUG
+ if (Str[i]==EOF)
+ {
+ cout<<"Premature end of depency file.\n";
+ Str[i]=0;
+ return;
+ }
+#endif
+ if (Str[i]=='\n')
+ break;
+ i++;
+ }
+ Str[i]='\0';
+}
+
+void mhmakefileparser::LoadAutoDepsFile(fileinfo *pDepFile)
+{
+ if (m_AutoDepFileLoaded && m_AutoDepFileLoaded==pDepFile)
+ return; /* This autodep file is already loaded. */
+
+ m_AutoDepFileLoaded=pDepFile;
+
+ FILE *pIn=fopen(pDepFile->GetFullFileName().c_str(),"rb");
+#ifdef _DEBUG
+ if (!pIn)
+ {
+ cerr << "Error opening autodep file "<<pDepFile->GetQuotedFullFileName()<<endl;
+ return;
+ }
+#endif
+ fread(&m_EnvMd5_32,sizeof(m_EnvMd5_32),1,pIn);
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Reading Env Md5 from "<<pDepFile->GetQuotedFullFileName()<<": "<<hex<<m_EnvMd5_32<<endl;
+#endif
+ char UsedEnvVars[1024];
+ ReadStr(pIn,UsedEnvVars);
+ SetVariable(USED_ENVVARS,UsedEnvVars);
+
+ char FileName[MAX_PATH];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ fileinfo *pTarget=GetFileInfo(FileName,m_MakeDir);
+ autodeps_entry_t &Autodeps=m_AutoDeps[pTarget];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ if (!g_ForceAutoDepRescan) /* If we are forcing the autodepscan we do not have to load the dependencies. */
+ {
+ fileinfo *pDep=GetFileInfo(FileName,m_MakeDir);
+ Autodeps.second.insert(pDep);
+ pTarget->AddDep(pDep);
+ }
+ ReadStr(pIn,FileName);
+ }
+ ReadStr(pIn,FileName);
+ }
+
+ uint32 Md5_32;
+ bool MakeNotDirty=true;
+ while (fread(&Md5_32,sizeof(Md5_32),1,pIn))
+ {
+ ReadStr(pIn,FileName);
+
+ fileinfo *pTarget=GetAbsFileInfo(FileName);
+
+ if (!pTarget->CompareMd5_32(0) && !pTarget->CompareMd5_32(Md5_32))
+ {
+ MakeNotDirty=false; /* BuildTarget had set the dirty flag, but since the md5 did not change it was a false dirty. This is for BuildTargets called before this routine */
+ #ifdef _DEBUG
+ cout << "Warning: trying to set to different md5's for Target "<<pTarget->GetQuotedFullFileName()<<" Old: "<<hex<<pTarget->GetCommandsMd5_32()<<" New: "<<Md5_32<<endl;
+ #endif
+ }
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Setting Md5 for Target "<<pTarget->GetQuotedFullFileName()<<" to "<<hex<<Md5_32<<endl;
+ #endif
+ pTarget->SetCommandsMd5_32(Md5_32); // If it was already there, just update the md5 value
+
+ AddTarget(pTarget);
+ }
+ if (MakeNotDirty)
+ ClearAutoDepsDirty();
+
+ fclose(pIn);
+}
+
+static void MakeDirs(const fileinfo *pDir)
+{
+ fileinfo *pParentDir=pDir->GetDir();
+ if (!pParentDir->GetDate().DoesExist())
+ { /* First make parent dirs */
+ MakeDirs(pParentDir);
+ }
+ if (!pDir->GetDate().DoesExist())
+ { /* Create directory */
+ mkdir(pDir->GetFullFileName().c_str(),S_IRWXU);
+ }
+}
+
+void mhmakefileparser::SaveAutoDepsFile()
+{
+ if (!IsAutoDepsDirty())
+ return;
+
+ if (g_Clean
+#ifdef _DEBUG
+ || g_DoNotExecute || g_GenProjectTree
+#endif
+ )
+ return; // do not save on clean or if no commands are executed
+
+ string DepFile=ExpandVar(AUTODEPFILE);
+ if (!DepFile.size())
+ {
+ return;
+ }
+ fileinfo *pDepFile=GetFileInfo(DepFile,m_MakeDir);
+
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout<<"Saving automatic dependency file "<<DepFile<<endl;
+#endif
+
+ FILE *pOut=fopen(pDepFile->GetFullFileName().c_str(),"wb");
+ if (!pOut)
+ {
+ /* Maybe it is because the directory does not exist, so try to create this first */
+ MakeDirs(pDepFile->GetDir());
+ pOut=fopen(pDepFile->GetFullFileName().c_str(),"wb");
+
+ if (!pOut)
+ {
+ #ifdef _DEBUG
+ if (!g_DoNotExecute)
+ #endif
+ cerr << "Error creating file "<<DepFile<<endl;
+ return;
+ }
+ }
+ // First update the USER_ENVVARS variable and then save it to the dep file together with the md5 string
+ CreateUSED_ENVVARS();
+ uint32 Md5_32=CreateEnvMd5_32();
+ fwrite(&Md5_32,sizeof(Md5_32),1,pOut);
+ fprintf(pOut,"%s\n",m_Variables[USED_ENVVARS].c_str());
+
+ autodeps_t::const_iterator It=m_AutoDeps.begin();
+ while (It!=m_AutoDeps.end())
+ {
+ if (!It->second.second.empty())
+ {
+ fprintf(pOut,"%s\n",It->first->GetFullFileName().c_str());
+ deps_t::const_iterator DepIt=It->second.second.begin();
+ while (DepIt!=It->second.second.end())
+ {
+ fprintf(pOut,"%s\n",(*DepIt)->GetFullFileName().c_str());
+ DepIt++;
+ }
+ fprintf(pOut,"\n");
+ }
+ It++;
+ }
+ /* Now save the Md5 strings */
+ fprintf(pOut,"\n");
+
+ set<const fileinfo *>::iterator pIt=m_Targets.begin();
+ while (pIt!=m_Targets.end())
+ {
+ if (!(*pIt)->CompareMd5_32(0))
+ {
+ (*pIt)->WriteMd5_32(pOut);
+ string FileName=(*pIt)->GetFullFileName();
+ fwrite(FileName.c_str(),FileName.size(),1,pOut);
+ fputc('\n',pOut);
+ }
+ pIt++;
+ }
+
+ fclose(pOut);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Uses the SKIPHEADERS variable to check if we have to skip the header in
+// the automatic dependency scanning
+
+bool mhmakefileparser::SkipHeaderFile(const string &FileName)
+{
+ if (!m_SkipHeadersInitialized)
+ {
+ m_SkipHeadersInitialized=true;
+ string HeadersToSkip=ExpandVar(SKIPHEADERS);
+ const char *pTmp=HeadersToSkip.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (Item.find('%')==string::npos)
+ {
+ m_SkipHeaders.insert(Item);
+ }
+ else
+ {
+ m_PercentHeaders.push_back(Item);
+ }
+ }
+ }
+ if (m_SkipHeaders.find(FileName)!=m_SkipHeaders.end())
+ return true;
+
+ vector<string>::const_iterator It=m_PercentHeaders.begin();
+ vector<string>::const_iterator ItEnd=m_PercentHeaders.end();
+ while (It!=ItEnd)
+ {
+ if (PercentMatch(FileName,*It++))
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::SetExport(const string &Var, const string &Val)
+{
+ m_Exports.insert(Var);
+#ifdef WIN32
+ if (!m_pEnv)
+ {
+ /* Environment not created yet, so create one */
+ char *pEnv=GetEnvironmentStrings();
+ char *pEnd=pEnv;
+ while (*pEnd)
+ {
+ while (*pEnd++);
+ }
+ size_t Len=pEnd-pEnv+1;
+ m_pEnv=(char*)malloc(Len);
+ memcpy(m_pEnv,pEnv,Len);
+ m_EnvLen=Len;
+ FreeEnvironmentStrings(pEnv);
+ }
+ /* First check if the variable is in the environment, if so remove it first */
+ char *pEnv=m_pEnv;
+ while (*pEnv)
+ {
+ const char *pVar=Var.c_str();
+ char *pStart=pEnv;
+ while (*pEnv!='=' && *pEnv==*pVar)
+ {
+ pEnv++; pVar++;
+ }
+ if (*pEnv=='=' && !*pVar)
+ {
+ /* Variable found, remove it */
+ while (*pEnv++);
+ m_EnvLen-=pEnv-pStart;
+ while (*pEnv)
+ {
+ while (*pEnv)
+ {
+ *pStart=*pEnv++;
+ pStart++;
+ }
+ *pStart=*pEnv++;
+ pStart++;
+ }
+ *pStart=*pEnv;
+ break;
+ }
+ while (*pEnv++);
+ }
+ size_t VarLen=Var.length();
+ size_t ValLen=Val.length();
+ size_t Extra=VarLen+ValLen+2;
+ /* Add the variable at the end */
+ m_pEnv=(char*)realloc(m_pEnv,m_EnvLen+Extra);
+ pEnv=m_pEnv+m_EnvLen-1;
+ memcpy(pEnv,Var.c_str(),VarLen);
+ pEnv+=VarLen;
+ *pEnv++='=';
+ memcpy(pEnv,Val.c_str(),ValLen);
+ pEnv+=ValLen;
+ *pEnv++='\0';
+ *pEnv++='\0';
+ m_EnvLen+=Extra;
+#else
+ if (!m_pEnv)
+ {
+ /* Environment not created yet, so create one */
+ char **pEnv=environ;
+ char **pEnd=pEnv;
+ int Len=1;
+ while (*pEnd)
+ {
+ Len++;
+ pEnd++;
+ }
+ m_EnvLen=Len;
+ m_pEnv=(char**)malloc(Len*sizeof(pEnv));
+ int i=0;
+ while (*pEnv)
+ {
+ m_pEnv[i]=strdup(*pEnv);
+ i++;
+ pEnv++;
+ }
+ m_pEnv[i]=NULL;
+ }
+ /* First check if the variable is in the environment, if so replace it */
+ char **pEnv=m_pEnv;
+ while (*pEnv)
+ {
+ const char *pVar=Var.c_str();
+ char *pStart=*pEnv;
+ char *pTmp=pStart;
+ while (*pTmp!='=' && *pTmp==*pVar)
+ {
+ pTmp++; pVar++;
+ }
+ if (*pTmp=='=' && !*pVar)
+ {
+ free(*pEnv);
+ *pEnv=strdup((Var+"="+Val).c_str());
+ break;
+ }
+ pEnv++;
+ }
+ if (!*pEnv)
+ {
+ // Add it at the end of the list
+ m_pEnv=(char**)realloc(m_pEnv,(m_EnvLen+1)*sizeof(*pEnv));
+ m_pEnv[m_EnvLen-1]=strdup((Var+"="+Val).c_str());
+ m_pEnv[m_EnvLen++]=NULL;
+ }
+
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::SetvPath(const string &Pattern, const string &Path)
+{
+ // First create the array of directory from the Path parameter. For now all entries need to
+ // be valid directories (extra checking). This could be improved by checking if there
+ // is a rule for creating the directory entry, but like first said, not for now.
+ refptr<fileinfoarray> pDirArray=new fileinfoarray;
+
+ const char *pTmp=Path.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item," \t:;");
+ if (!Item.empty())
+ {
+ fileinfo *pDir=GetFileInfo(Item,m_MakeDir);
+ if (!pDir->IsDir())
+ {
+ #ifdef WIN32
+ // On windows this could happen if a full pathname was specified, so we try together
+ // with the next item to check if this is not a valid directories
+ // but only if the the length of the item was 1 namely the driver letter
+ if (Item.length()==1)
+ {
+ string Rest;
+ pTmp=NextItem(pTmp,Rest," \t:;");
+ pDir=GetFileInfo(Item+":"+Rest,m_MakeDir);
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo && !pDir->IsDir())
+ throw(pDir->GetFullFileName()+" is not a valid directory.");
+ #endif
+ }
+ #ifdef _DEBUG
+ else if (g_PrintAdditionalInfo)
+ cout << pDir->GetFullFileName() << " is not a valid directory.\n";
+ #endif
+ #else
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << pDir->GetFullFileName() << " is not a valid directory.\n";
+ #endif
+ #endif
+ }
+ pDirArray->push_back(pDir);
+ }
+ }
+ m_vPath.push_back(pair<string, refptr<fileinfoarray> >(Pattern,pDirArray));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Search for the target using the vPath
+fileinfo* mhmakefileparser::SearchvPath(const fileinfo* pTarget)
+{
+ string TargetName=pTarget->GetName();
+ vector< pair< string, refptr<fileinfoarray> > >::iterator vPathIt=m_vPath.begin();
+ while (vPathIt!=m_vPath.end())
+ {
+ matchres Res;
+
+ if (PercentMatch(TargetName,vPathIt->first,&Res))
+ {
+ fileinfoarray::iterator pIt=vPathIt->second->begin();
+ while (pIt!=vPathIt->second->end())
+ {
+ fileinfo* pNewTarget=GetFileInfo(TargetName,*pIt);
+ mh_time_t TargetDate=StartBuildTarget(pNewTarget,false);
+ if (!TargetDate.IsDateValid())
+ TargetDate=WaitBuildTarget(pNewTarget);
+ if (pNewTarget->GetDate().DoesExist())
+ return pNewTarget;
+ pIt++;
+ }
+ }
+ vPathIt++;
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//Checks if the variables retreived from the environment or command-line have been
+//changed. Do this at late as possible because they can also be changed in theLexer
+//makefiles.
+//
+void mhmakefileparser::CheckEnv(void)
+{
+ if (CompareEnv())
+ {
+ #ifdef _DEBUG
+ if (!g_GenProjectTree)
+ cout << "Rebuilding everything of "<< m_MakeDir->GetQuotedFullFileName() <<" because environment and/or command-line variables have been changed.\n";
+ #endif
+ SetRebuildAll();
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//Create a Md5 string from m_GlobalCommandLineVars and USED_ENVVARS
+//
+
+
+static bool SkipVar(const string &Var)
+{
+ if (Var==WC_REVISION)
+ return true;
+ if (Var==WC_URL)
+ return true;
+ if (Var==MAKE)
+ return true;
+ return false;
+}
+
+#define DBGOUT(stuff)
+
+uint32 mhmakefileparser::CreateEnvMd5_32() const
+{
+ md5_context ctx;
+ string Md5;
+ string EnvVars=ExpandVar(USED_ENVVARS);
+ const char *pTmp=EnvVars.c_str();
+
+ // Now create the md5 string
+ md5_starts( &ctx );
+
+ DBGOUT(cout << "MD5 of " << m_MakeDir->GetQuotedFullFileName() << endl);
+
+ while (*pTmp)
+ {
+ string Var;
+ pTmp=NextItem(pTmp,Var,";");
+ if (!SkipVar(Var))
+ {
+ string Val=ExpandVar(Var);
+ transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
+ DBGOUT(cout << GetMakeDir()->GetQuotedFullFileName() << " -> Setting GetFromEnv var " << Var << " to " << Val << endl);
+ md5_update( &ctx, (uint8 *) Var.c_str(), (unsigned long)Var.size());
+ md5_update( &ctx, (uint8 *) "=", 1);
+ md5_update( &ctx, (uint8 *) Val.c_str(), (unsigned long)Val.size());
+ }
+ }
+
+ return md5_finish32( &ctx);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+
+bool mhmakefileparser::CompareEnv() const
+{
+ uint32 Md5_32=CreateEnvMd5_32();
+#ifdef _DEBUG
+ if (!g_GenProjectTree && Md5_32!=m_EnvMd5_32)
+ cout << "Environment has been changed: "<<hex<<m_EnvMd5_32<<" to "<<Md5_32<<endl;
+#endif
+ return Md5_32!=m_EnvMd5_32;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+mh_time_t mhmakefileparser::m_sBuildTime;
+
+void mhmakefileparser::InitBuildTime()
+{
+ m_sBuildTime=time(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns a variable from the environment or from the command line and adds it the m_UsedEnvVars
+string mhmakefileparser::GetFromEnv(const string &Var, bool Cache) const
+{
+ /* First we look into the command line variables, before we are looking in the environment */
+ map<string,string>::const_iterator pLineFind=m_CommandLineVars.find(Var);
+ if (pLineFind!=m_CommandLineVars.end())
+ {
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pLineFind->second;
+ return pLineFind->second;
+ }
+
+ const char *pEnv=getenv(Var.c_str());
+ if (!pEnv)
+ {
+ return g_EmptyString;
+ }
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pEnv;
+ return pEnv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Creates the variable USER_ENVVARS to be saved in the autodeps file
+//
+
+void mhmakefileparser::CreateUSED_ENVVARS()
+{
+
+ set<string> Variables;
+
+ set<string>::const_iterator It=m_UsedEnvVars.begin();
+ set<string>::const_iterator ItEnd=m_UsedEnvVars.end();
+
+ while (It!=ItEnd)
+ {
+ string Var=*It;
+ if (!SkipVar(Var))
+ {
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ Variables.insert(Var);
+ }
+ It++;
+ }
+
+ It=m_Exports.begin();
+ ItEnd=m_Exports.end();
+
+ while (It!=ItEnd)
+ {
+ string Var=*It;
+ if (!SkipVar(Var))
+ {
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ Variables.insert(Var);
+ }
+ It++;
+ }
+
+ map<string,string>::const_iterator CLItEnd=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.end();
+ map<string,string>::const_iterator CLIt=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.begin();
+ while (CLIt!=CLItEnd)
+ {
+ string Var=CLIt->first;
+ if (!SkipVar(Var))
+ {
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ Variables.insert(Var);
+ }
+ CLIt++;
+ }
+
+ It=Variables.begin();
+ ItEnd=Variables.end();
+ string Val;
+ while (It!=ItEnd)
+ {
+ Val+=*It;
+ Val+=";";
+ It++;
+ }
+
+ m_Variables[USED_ENVVARS]=Val;
+}
+
+static string s_TrueString("1");
+static string s_FalseString("0");
+
+static string AndExpression(const string &First, const string &Second)
+{
+ if (First.empty() || First==s_FalseString)
+ {
+ return g_EmptyString;
+ }
+ else
+ {
+ return Second.empty() || Second==s_FalseString ? g_EmptyString : s_TrueString;
+ }
+}
+
+static string OrExpression(const string &First, const string &Second)
+{
+ if (First.empty() || First==s_FalseString)
+ {
+ return Second.empty() || Second==s_FalseString ? g_EmptyString : s_TrueString;
+ }
+ else
+ {
+ return s_TrueString;
+ }
+}
+
+string mhmakefileparser::ResolveExpression(const string &InExpr,string &Rest) const
+{
+ unsigned i=0;
+ string Ret;
+ string Expr=InExpr;
+
+ Rest=g_EmptyString;
+
+ while (i<Expr.length())
+ {
+ while (strchr(" \t\r",Expr[i])) i++;
+ switch (Expr[i])
+ {
+ case '!':
+ if (Expr[i+1]!='=')
+ {
+ Ret=ResolveExpression(Expr.substr(i+1),Expr);
+ Ret = Ret.empty() || Ret==s_FalseString ? s_TrueString : g_EmptyString;
+ i=0;
+ }
+ else
+ {
+ Ret = Ret!=ResolveExpression(Expr.substr(i+2),Expr) ? s_TrueString : g_EmptyString;
+ i=0;
+ }
+ break;
+ case '&':
+ if (Expr[i+1]!='&')
+ {
+ Ret+=Expr[i++];
+ }
+ else
+ {
+ Ret=AndExpression(Ret,ResolveExpression(Expr.substr(i+2),Expr));
+ i=0;
+ }
+ break;
+ case '|':
+ if (Expr[i+1]!='|')
+ {
+ Ret+=Expr[i++];
+ }
+ else
+ {
+ Ret=OrExpression(Ret,ResolveExpression(Expr.substr(i+2),Expr));
+ i=0;
+ }
+ break;
+ case '(':
+ if (Ret=="defined")
+ {
+ Ret=IsDefined(ResolveExpression(Expr.substr(i+1),Expr)) ? s_TrueString : g_EmptyString;
+ }
+ else
+ {
+ Ret+=ResolveExpression(Expr.substr(i+1),Expr);
+ }
+ i=0;
+ break;
+ case ')':
+ Rest=Expr.substr(i+1);
+ return Ret;
+ break;
+ case '"':
+ {
+ i++;
+ while (i<Expr.length())
+ {
+ char Char=Expr[i++];
+ if (Char=='"')
+ break;
+ Ret+=Char;
+ }
+ }
+ break;
+ case '=':
+ if (Expr[i+1]!='=')
+ {
+ Ret+=Expr[i++];
+ }
+ else
+ {
+ Ret = Ret==ResolveExpression(Expr.substr(i+2),Expr) ? s_TrueString : g_EmptyString;
+ i=0;
+ }
+ break;
+ default:
+ Ret+=Expr[i++];
+ break;
+ }
+ }
+ return Ret;
+}
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsExprTrue(const string &EqualExpr) const
+{
+ string Expr=ExpandExpression(EqualExpr);
+ string Rest;
+ Expr=ResolveExpression(Expr,Rest);
+ if (Expr.empty() || Expr==s_FalseString)
+ return false;
+ else
+ return true;
+}
+
diff --git a/tools/mhmake/src/mhmakefileparser.h b/tools/mhmake/src/mhmakefileparser.h
new file mode 100644
index 000000000..f1c9cb280
--- /dev/null
+++ b/tools/mhmake/src/mhmakefileparser.h
@@ -0,0 +1,383 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __MHMAKEFILE_PARSER__
+#define __MHMAKEFILE_PARSER__
+
+#include "fileinfo.h"
+#include "util.h"
+#include "commandqueue.h"
+
+class rule;
+
+class mhmakeFlexLexer;
+
+struct TOKENVALUE
+{
+ string theString;
+ int ival;
+};
+
+class mhmakefileparser;
+typedef string (mhmakefileparser::*function_f)(const string &, const string *) const;
+
+struct funcdef
+{
+ const char *szFuncName;
+ function_f pFunc;
+};
+
+class fileinfoarray : public refbase,public vector<fileinfo*>
+{
+};
+
+typedef set<fileinfo*> deps_t;
+typedef pair< bool, deps_t > autodeps_entry_t;
+typedef map<fileinfo*, autodeps_entry_t > autodeps_t;
+
+class mhmakefileparser : public refbase
+{
+
+private:
+ static commandqueue sm_CommandQueue;
+
+ mhmakeFlexLexer *m_ptheLexer;
+ int m_yyloc;
+ fileinfo *m_RuleThatIsBuild;
+ vector<string> m_ToBeIncludeAfterBuild;
+ vector<string> m_MakefilesToLoad;
+ fileinfo* m_AutoDepFileLoaded;
+ mh_time_t m_Date;
+ uint32 m_EnvMd5_32; /* Cached Md5_32 value of the userd environment variables */
+#ifdef _DEBUG
+ int m_ImplicitSearch;
+#endif
+ map<string,string> m_CommandCache;
+
+protected:
+ map<string,string> m_Variables;
+ map<string,string> m_CommandLineVars;
+ TOKENVALUE m_theTokenValue;
+ const fileinfo *m_MakeDir;
+ refptr<rule> m_pCurrentRule;
+ refptr<fileinfoarray> m_pCurrentItems;
+ refptr<fileinfoarray> m_pCurrentDeps;
+ fileinfo* m_FirstTarget;
+ fileinfoarray m_IncludedMakefiles;
+ refptr<fileinfoarray> m_pIncludeDirs;
+ string m_IncludeDirs;
+
+ bool m_DoubleColonRule;
+ bool m_AutoDepsDirty;
+ bool m_ForceAutoDepRescan;
+ set<string> m_SkipHeaders; // Headers to skip
+ vector<string> m_PercentHeaders; // Percent specification of headers to skip
+ bool m_SkipHeadersInitialized; // true when previous 2 variables are initialized
+
+ bool m_RebuildAll; /* true when to rebuild all targets of this makefile */
+
+ set<string> m_UsedEnvVars; // Array containing a list of variables that are taken from the environment (This is used for rebuild checking)
+ set<string> m_Exports; // Array containing a list of exported variables in the makefile (This is used for rebuild checking)
+ vector< pair< string, refptr<fileinfoarray> > > m_vPath;
+#ifdef WIN32
+ char *m_pEnv; // New environment in case the makefile exports variables
+#else
+ char **m_pEnv; // New environment in case the makefile exports variables
+#endif
+ size_t m_EnvLen; // Current length of m_pEnv
+
+ autodeps_t m_AutoDeps;
+ set< const fileinfo* > m_Targets; // List of targets that are build by this makefile
+
+ static mh_time_t m_sBuildTime;
+private:
+ string ResolveExpression(const string &InExpr,string &Rest) const;
+
+public:
+#ifdef _DEBUG
+ deque<fileinfo*> m_TargetStack; /* Used to detect circular dependencies */
+#endif
+
+ mhmakefileparser(const map<string,string> &CommandLineVars)
+ : m_CommandLineVars(CommandLineVars)
+ ,m_AutoDepsDirty(false)
+ ,m_ForceAutoDepRescan(false)
+ ,m_SkipHeadersInitialized(false)
+ ,m_RebuildAll(false)
+ ,m_EnvMd5_32(0)
+ ,m_pEnv(NULL)
+ ,m_FirstTarget(NULL)
+ ,m_RuleThatIsBuild(NULL)
+ ,m_AutoDepFileLoaded(NULL)
+ #ifdef _DEBUG
+ ,m_ImplicitSearch(0)
+ #endif
+ {
+ if (!m_FunctionsInitialised) InitFuncs();
+ SetVariable("MAKE_VERSION",MHMAKEVER);
+ SetVariable(OBJEXTVAR,OBJEXT);
+ SetVariable(EXEEXTVAR,EXEEXT);
+ }
+
+ /* Needed if you only want to use the searchcommand and execommand functions */
+ mhmakefileparser(const fileinfo *pMakeDir) :
+ m_MakeDir(pMakeDir)
+ , m_AutoDepsDirty(false)
+ , m_pEnv(NULL)
+ {}
+
+ static void SetNrParallelBuilds(int NrParallelBuilds)
+ {
+ sm_CommandQueue.SetNrParallelBuilds(NrParallelBuilds);
+ }
+
+ bool CompareEnv() const;
+ uint32 CreateEnvMd5_32() const;
+ string GetFromEnv(const string &Var,bool Cache=true) const;
+ void CreateUSED_ENVVARS();
+
+ void SetExport(const string & Var, const string & Val);
+ void SetvPath(const string & Pattern, const string & Path);
+
+ void CheckEnv(void);
+
+ #ifdef _DEBUG
+ int ImplicitSearch() const
+ {
+ return m_ImplicitSearch;
+ }
+ #endif
+
+ void SetAutoDepsDirty()
+ {
+ m_AutoDepsDirty=true;
+ }
+
+ void ClearAutoDepsDirty()
+ {
+ m_AutoDepsDirty=false;
+ }
+ bool IsAutoDepsDirty() const
+ {
+ return m_AutoDepsDirty;
+ }
+
+ void SetRebuildAll()
+ {
+ m_RebuildAll=true;
+ SetAutoDepsDirty(); /* This is to be sure that all new calculated md5 strings are saved. */
+ }
+
+ void SetVariable(string Var,string Val)
+ {
+ m_Variables[Var]=Val;
+ }
+ void DeleteVariable(string Var)
+ {
+ m_Variables.erase(Var);
+ }
+ void EnableAutoDepRescan(void)
+ {
+ m_ForceAutoDepRescan=true;
+ SetAutoDepsDirty();
+ }
+ bool ForceAutoDepRescan(void)
+ {
+ return m_ForceAutoDepRescan;
+ }
+ void SetRuleThatIsBuild(fileinfo *pTarget)
+ {
+ m_RuleThatIsBuild=pTarget;
+ }
+ void ClearRuleThatIsBuild()
+ {
+ m_RuleThatIsBuild=NULL;
+ }
+ void GetAutoDepsIfNeeded(fileinfo *pTarget, const fileinfo *pFirstDep);
+ void UpdateAutomaticDependencies(fileinfo *pTarget);
+ void UpdateNoRuleAutomaticDependencies(fileinfo *pTarget);
+ const refptr<fileinfoarray> GetIncludeDirs() const;
+ void GetAutoDeps(const fileinfo *pFirstDep, deps_t &Autodeps);
+ void SaveAutoDepsFile();
+ void LoadAutoDepsFile(fileinfo *pDepFile);
+ bool SkipHeaderFile(const string &FileName);
+ void InitEnv() const;
+
+ virtual ~mhmakefileparser() /* virtual to be sure the correct destructor is called when we delete with a pointer to this class which in reality is a pointer to a derived class */
+ {
+ SaveAutoDepsFile();
+#ifndef WIN32
+ char **pEnv=m_pEnv;
+ if (pEnv)
+ while (*pEnv) free(*pEnv++);
+#endif
+ free(m_pEnv);
+ }
+ virtual int yylex(void);
+ virtual void yyerror(const char *m);
+ virtual int yyparse()
+ {
+ throw("Please derive if you want to execute yyparse.");
+ }
+
+ int ParseFile(const fileinfo *pFileInfo,const fileinfo *pMakeDir=NULL);
+
+ /* Functions to handle variables */
+ bool IsDefined(const string &Var) const;
+ bool IsEqual(const string &EqualExpr) const;
+ bool IsExprTrue(const string &EqualExpr) const;
+ string ExpandExpression(const string &Expr) const;
+ string ExpandExpressionRecurse(const string &Expr, bool &Recurse) const;
+ string ExpandMacro(const string &Expr, bool &Recurse) const;
+ string ExpandVar(const string &Var) const;
+
+ void PrintVariables(bool Expand=false) const;
+
+ /* Functions for macro functions */
+ static funcdef m_FunctionsDef[];
+ static map<string,function_f> m_Functions;
+ static bool m_FunctionsInitialised;
+ static void InitFuncs(void);
+ string f_filter(const string &, const string *pOriExpr) const;
+ string f_call(const string &, const string *pOriExpr) const;
+ string f_if(const string &, const string *pOriExpr) const;
+ string f_findstring(const string &, const string *pOriExpr) const;
+ string f_firstword(const string &, const string *pOriExpr) const;
+ string f_wildcard(const string &, const string *pOriExpr) const;
+ string f_subst(const string &, const string *pOriExpr) const;
+ string f_patsubst(const string &, const string *pOriExpr) const;
+ string f_concat(const string &, const string *pOriExpr) const;
+ string f_basename(const string & Arg, const string *pOriExpr) const;
+ string f_notdir(const string & Arg, const string *pOriExpr) const;
+ string f_dir(const string & Arg, const string *pOriExpr) const;
+ string f_shell(const string & Arg, const string *pOriExpr) const;
+ string f_relpath(const string & Arg, const string *pOriExpr) const;
+ string f_toupper(const string & Arg, const string *pOriExpr) const;
+ string f_tolower(const string & Arg, const string *pOriExpr) const;
+ string f_exist(const string & Arg, const string *pOriExpr) const;
+ string f_filesindirs(const string & Arg, const string *pOriExpr) const;
+ string f_fullname(const string & Arg, const string *pOriExpr) const;
+ string f_addprefix(const string & Arg, const string *pOriExpr) const;
+ string f_addsuffix(const string & Arg, const string *pOriExpr) const;
+ string f_filterout(const string & Arg, const string *pOriExpr) const;
+ string f_word(const string & Arg, const string *pOriExpr) const;
+ string f_words(const string & Arg, const string *pOriExpr) const;
+ string f_strip(const string & Arg, const string *pOriExpr) const;
+ string f_which(const string & Arg, const string *pOriExpr) const;
+ string f_foreach(const string & Arg, const string *pOriExpr) const;
+
+ fileinfo* GetFirstTarget() const
+ {
+ return m_FirstTarget;
+ }
+
+ const fileinfo *GetMakeDir() const
+ {
+ return m_MakeDir;
+ }
+
+ mh_time_t GetDate(void) const
+ {
+ return m_Date;
+ }
+
+ void UpdateDate(mh_time_t Date)
+ {
+ if (Date.IsNewer(m_Date))
+ m_Date=Date;
+ }
+
+ void AddTarget(const fileinfo* pTarget)
+ {
+ m_Targets.insert(pTarget);
+ }
+ /* Starts building the target,
+ returns 0 when target build is not finished,
+ returns the date of the target when target is build, especially important for phony rules, since this will be the youngest date of all dependencies */
+ mh_time_t StartBuildTarget(fileinfo* pTarget, bool bCheckTargetDir=true);
+ /* Waits for the target being build, returns the date of the target. Not needed to be cald when StartBuildTarget returned a value different from zero */
+ mh_time_t WaitBuildTarget(fileinfo* pTarget);
+
+ fileinfo* SearchvPath(const fileinfo* pTarget); /* Search for the target using the vPath */
+
+ /* Use the following command when you want to wait for the target is built */
+ mh_time_t BuildTarget(fileinfo* pTarget, bool bCheckTargetDir=true)
+ {
+ mh_time_t TargetDate=StartBuildTarget(pTarget,bCheckTargetDir);
+ if (!TargetDate.IsDateValid())
+ TargetDate=WaitBuildTarget(pTarget);
+ return TargetDate;
+ }
+
+ void BuildDependencies(const refptr<rule> &pRule, fileinfo* pTarget, mh_time_t TargetDate, mh_time_t &YoungestDate, bool &MakeTarget);
+
+ void BuildIncludedMakefiles();
+
+ void AddIncludedMakefile(fileinfo* pMakeInfo)
+ {
+ UpdateDate(pMakeInfo->GetDate());
+ m_IncludedMakefiles.push_back(pMakeInfo);
+ }
+ fileinfoarray &GetIncludedMakefiles()
+ {
+ return m_IncludedMakefiles;
+ }
+
+ void IncludeAfterBuild(const string &IncludeFile)
+ {
+ m_ToBeIncludeAfterBuild.push_back(IncludeFile);
+ }
+ void ParseBuildedIncludeFiles();
+
+ void AddMakefileToMakefilesToLoad(const string &Makefile)
+ {
+ m_MakefilesToLoad.push_back(Makefile);
+ }
+ vector<string>& GetMakefilesToLoad()
+ {
+ return m_MakefilesToLoad;
+ }
+ void AddRule();
+
+ mh_pid_t ExecuteCommand(string Command, bool &IgnoreError, string *pOutput=NULL);
+ mh_pid_t ExecuteCommand(string Command, string *pOutput=NULL)
+ {
+ bool IgnoreError;
+ return ExecuteCommand(Command, IgnoreError, pOutput);
+ }
+ string GetFullCommand(string Command);
+ void CreatePythonExe(const string &FullCommand);
+
+ static void InitBuildTime();
+
+ void SplitToItems(const string &String, vector<fileinfo*> &Items) const;
+ mh_pid_t DeleteFiles(const string &Params) const;
+ mh_pid_t CopyFiles(const string &Params) const;
+ mh_pid_t TouchFiles(const string &Params) const;
+ mh_pid_t EchoCommand(const string &Params) const;
+ string SearchCommand(const string &Command, const string &Extension="") const;
+ const string &GetPythonExe() const;
+ int SearchPath(const char *szCommand, const char *pExt, size_t Len, char *szFullCommand,char **pFilePart) const;
+ mh_pid_t OsExeCommand(const string &Command, const string &Params, bool IgnoreError, string *pOutput) const;
+};
+
+
+#endif
+
diff --git a/tools/mhmake/src/mhmakelexer.l b/tools/mhmake/src/mhmakelexer.l
new file mode 100644
index 000000000..8cf0f1680
--- /dev/null
+++ b/tools/mhmake/src/mhmakelexer.l
@@ -0,0 +1,987 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* -------------- declaration section -------------- */
+%{
+
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+
+static uint32 LoadMakMd5(fileinfo *pTarget)
+{
+ string FileName=pTarget->GetFullFileName();
+ FileName+=".md5_32";
+ FILE *pFile=fopen(FileName.c_str(),"rb");
+ if (!pFile)
+ return 0;
+ uint32 Md5_32=0;
+ fread(&Md5_32,sizeof(Md5_32),1,pFile);
+ fclose(pFile);
+ pTarget->SetCommandsMd5_32(Md5_32);
+ return Md5_32;
+}
+
+static void SaveMakMd5(fileinfo *pTarget)
+{
+ string FileName=pTarget->GetFullFileName();
+ FileName+=".md5_32";
+ FILE *pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ throw string("Error creating file ")+FileName;
+ }
+ pTarget->WriteMd5_32(pFile);
+ fclose(pFile);
+}
+
+static void ReplaceCurlyBraces(string &String)
+{
+ int Pos=String.find_first_of('{',0);
+ if (Pos!=string::npos)
+ {
+ /* if not { found, } will not be there eather (or it is a valid syntax, so it may generate an error) */
+ do
+ {
+ String.replace(Pos,1,1,'(');
+ Pos=String.find_first_of('{',Pos);
+ }
+ while (Pos!=string::npos);
+ Pos=0;
+ while ((Pos=String.find_first_of('}',Pos))!=string::npos)
+ String.replace(Pos,1,1,')');
+ }
+}
+
+#define YY_DECL int mhmakeFlexLexer::yylex(TOKENVALUE &theValue)
+
+%}
+
+%option prefix="mhmake"
+%option never-interactive
+%option 8bit
+%option c++
+%option full
+%option noyywrap
+%option warn
+
+/* -------------- rules section -------------- */
+%x INCLUDE IFDEF IF IFNDEF SKIPUNTILELSEORENDIF QUOTE MAKEEXPRES SINGLEQUOTE COMMANDPARSE
+%x IFEQ IFNEQ ERRORMACRO MESSAGEMACRO REPARSEMACRO LOAD_MAKEFILE
+%x DEFINE
+
+%%
+
+ /*---------------------------------------------------------------------------*/
+[ \t\r]*\n[ ][ \t]* {
+ yy_set_bol(1); // Make sure the next rule also matches the ^
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+[ \t\r]*\n {
+ PRINTF(("%s %d: NEWLINE:\n",m_InputFileName.c_str(),yylineno));
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[s\-]?include {
+ PRINTF(("%s %d: INCLUDE: ",m_InputFileName.c_str(),yylineno));
+ BEGIN(INCLUDE);
+ unsigned i=0;
+ while (strchr(" \t",yytext[i])) i++;
+ if (strchr("-s",yytext[i]))
+ m_IgnoreIncludeError=true;
+ else
+ m_IgnoreIncludeError=false;
+ return mhmakeparser::INCLUDEMAK; // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
+}
+
+ /*****************************************************************************/
+<INCLUDE>[ \t]* /* eat the whitespace */
+ /*---------------------------------------------------------------------------*/
+<INCLUDE>[^\r\n]+ { /* got the include file name */
+ mhmakeparser *pParser=GetParser();
+
+ /* replace the {} by () before expanding */
+ string IncludeFileNames(yytext);
+ ReplaceCurlyBraces(IncludeFileNames);
+ IncludeFileNames=pParser->ExpandExpression(IncludeFileNames);
+ PRINTF(("%s -> %s\n",yytext,IncludeFileNames.c_str()));
+
+ const char *pTmp=IncludeFileNames.c_str();
+ while (*pTmp)
+ {
+ string IncludeFileName;
+ pTmp=NextItem(pTmp,IncludeFileName);
+ if (!IncludeFileName.empty())
+ {
+ PRINTF(("%s -> %s\n",yytext,IncludeFileName.c_str()));
+ fileinfo *pInclude=GetFileInfo(IncludeFileName,pParser->GetMakeDir());
+ /* Already build the include file, in case we already have a rule for it. */
+ if (pInclude->GetRule())
+ {
+ uint32 Md5_32=LoadMakMd5(pInclude);
+ pParser->BuildTarget(pInclude);
+ if (!pInclude->CompareMd5_32(Md5_32))
+ SaveMakMd5(pInclude);
+ }
+
+ pParser->AddIncludedMakefile(pInclude);
+
+ string strToInclude=pInclude->GetFullFileName();
+ INSTACK *pStackElem=new INSTACK(YY_CURRENT_BUFFER, strToInclude, m_InputFileName, yylineno);
+ if ( pStackElem->fail() )
+ {
+ delete pStackElem;
+ if (!m_IgnoreIncludeError)
+ {
+ mystack::reverse_iterator StackIt=m_IncludeStack.rbegin();
+ while (StackIt!=m_IncludeStack.rend())
+ {
+ cout<<" in "<<(*StackIt)->m_FileName<<" ("<<(*StackIt)->yylineno<<")";
+ StackIt++;
+ }
+ cout<<endl;
+ cout<<"Warning error opening file "<<strToInclude<<" in "<<m_InputFileName<<" ("<<yylineno<<")\n";
+ pParser->IncludeAfterBuild(strToInclude);
+ }
+ else
+ pInclude->SetPhony(); /* To be sure that no message is printed when mhmake is trying to build the file later */
+ }
+ else
+ {
+ m_IncludeStack.push(pStackElem);
+ yylineno=1;
+
+ m_InputFileName=strToInclude;
+
+ yypush_buffer_state(yy_create_buffer( pStackElem->GetStream(), YY_BUF_SIZE ));
+ yyrestart(pStackElem->GetStream());
+ }
+
+ }
+ }
+
+ BEGIN(INITIAL);
+}
+
+ /*---------------------------------------------------------------------------*/
+load_makefile {
+ PRINTF(("%s %d: LOAD_MAKEFILE:\n",m_InputFileName.c_str(),yylineno));
+ BEGIN(LOAD_MAKEFILE);
+ return mhmakeparser::NEWLINE; // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
+}
+
+ /*****************************************************************************/
+<LOAD_MAKEFILE>[^\r\n]+ {
+ string ListOfMakefiles((const char*)yytext);
+ ReplaceCurlyBraces(ListOfMakefiles);
+ ListOfMakefiles=GetParser()->ExpandExpression(ListOfMakefiles);
+ PRINTF(("%s %d: LOAD_MAKEFILE: '%s'\n",m_InputFileName.c_str(),yylineno,ListOfMakefiles.c_str()));
+
+ const char *pTmp=ListOfMakefiles.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextCharItem(pTmp,Item,';');
+ if (Item.empty())
+ {
+ throw m_InputFileName + "(" + stringify(yylineno) + "): Error in load_makefile statement";
+ }
+ GetParser()->AddMakefileToMakefilesToLoad(Item);
+ }
+
+}
+ /*---------------------------------------------------------------------------*/
+<LOAD_MAKEFILE>\r?\n {
+ yylineno++;
+ BEGIN(INITIAL);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]+ {
+ PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),yylineno));
+ return mhmakeparser::SPACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*=[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ yylineno++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::EQUAL;
+}
+
+[ \t]*=[ \t]* {
+ PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::EQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*:=[ \t]*\\[ \t\r]*\n[ \t]* {
+ yylineno++;
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::IMEQUAL;
+}
+
+[ \t]*:=[ \t]* {
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::IMEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*\?=[ \t]*\\[ \t\r]*\n[ \t]* {
+ yylineno++;
+ PRINTF(("%s %d: OPTEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::OPTEQUAL;
+}
+
+[ \t]*\?=[ \t]* {
+ PRINTF(("%s %d: OPTEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::OPTEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*\+=[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ yylineno++;
+ return mhmakeparser::PEQUAL;
+}
+
+[ \t]*\+=[ \t]* {
+ PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::PEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*;[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ m_curtoken=g_EmptyString;
+ yylineno++;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+[ \t]*;[ \t]* {
+ PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ m_curtoken=g_EmptyString;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*::[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ yylineno++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOUBLECOLON;
+}
+
+[ \t]*::[ \t]* {
+ PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOUBLECOLON;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*:[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ yylineno++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COLON;
+}
+
+[ \t]*:[ \t]* {
+ PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COLON;
+}
+
+ /*---------------------------------------------------------------------------*/
+, {
+ PRINTF(("%s %d: COMMA: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COMMA;
+}
+
+ /*---------------------------------------------------------------------------*/
+^endif {
+ if (m_IndentStack.size())
+ {
+ m_IndentStack.pop();
+ PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ }
+ else
+ {
+ throw string("Unexpected endif at line ") + stringify(yylineno) + " of " + m_InputFileName;
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+^ifdef[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFDEF);
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+^ifdef[ \t]+ {
+ BEGIN(IFDEF);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^if[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IF);
+ yylineno++;
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+^if[ \t]+ {
+ BEGIN(IF);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^ifndef[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFNDEF);
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+^ifndef[ \t]+ {
+ BEGIN(IFNDEF);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^ifeq[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFEQ);
+ m_curtoken=g_EmptyString;
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+^ifeq[ \t]+ {
+ BEGIN(IFEQ);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^ifneq[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFNEQ);
+ m_curtoken=g_EmptyString;
+ yylineno++;
+ return mhmakeparser::NEWLINE;
+}
+
+^ifneq[ \t]+ {
+ BEGIN(IFNEQ);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^else[ \t]* {
+ if (m_IndentStack.size() && (!m_IndentStack.top()))
+ {
+ PRINTF(("%s %d: skipping else: depth %d\n",m_InputFileName.c_str(),yylineno,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ m_IndentStack.top()=1;
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+ else
+ {
+ throw string("Unexpected else at line ") + stringify(yylineno) + " of file " + m_InputFileName;
+ }
+}
+
+ /*****************************************************************************/
+<IFEQ>\n {
+ yyless(0);
+ m_IndentStack.push(0);
+ if (GetParser()->IsEqual(m_curtoken))
+ {
+ PRINTF(("%s %d: Not Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+<IF,IFEQ,IFNEQ>[ \t]*\\[ \t\r]*\n[ \t]* { yylineno++; m_curtoken += g_SpaceString;}
+
+<IF,IFEQ,IFNEQ>\r /* skip */
+
+<IF,IFEQ,IFNEQ>[^\\\r\n{}\$]+ |
+<IF,IFEQ,IFNEQ>\$ |
+<IF,IFEQ,IFNEQ>\\ { m_curtoken += (const char *)yytext; }
+
+ /*****************************************************************************/
+<IFNEQ>\n {
+ yyless(0);
+ m_IndentStack.push(0);
+ if (!GetParser()->IsEqual(m_curtoken))
+ {
+ PRINTF(("%s %d: Not Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<IF>[ \t\r]*[a-zA-Z0-9_]+[ \t\r]*\n {
+ yyless(yyleng-1);
+ m_IndentStack.push(0);
+#ifndef WIN32
+ int lastidx=yyleng-1;
+ if (yytext[lastidx]=='\r')
+ yytext[lastidx]='\0';
+#endif
+ string Val=GetParser()->ExpandVar((const char *)yytext);
+ if (Val.empty() || Val=="0")
+ {
+ PRINTF(("%s %d: Skipping if %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+ else
+ {
+ PRINTF(("%s %d: Not Skipping if %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<IF>\n {
+ yyless(0);
+ m_IndentStack.push(0);
+ if (GetParser()->IsExprTrue(m_curtoken))
+ {
+ PRINTF(("%s %d: Not Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str(),m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<IFDEF,IFNDEF>[ \t\r]* /* skip */
+
+ /*---------------------------------------------------------------------------*/
+<IFDEF>[a-zA-Z0-9_]+ {
+ m_IndentStack.push(0);
+ if (GetParser()->IsDefined((const char *)yytext))
+ {
+ PRINTF(("%s %d: Not Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<IFNDEF>[a-zA-Z0-9_]+ {
+ m_IndentStack.push(0);
+ if (!GetParser()->IsDefined((const char *)yytext)) {
+ PRINTF(("%s %d: Not Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<SKIPUNTILELSEORENDIF>\n[ ]*endif {
+ yylineno++;
+ if (!m_IndentStack.size())
+ {
+ throw string("Unexpected endif at line ") + stringify(yylineno) + " of " + m_InputFileName;
+ }
+ else
+ {
+ m_IndentStack.pop();
+ PRINTF(("%s %d: endif: depth %d\n",m_InputFileName.c_str(),yylineno,m_IndentStack.size()));
+ if (m_IndentStack.size()==m_IndentSkip-1) BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*else {
+ yylineno++;
+ PRINTF(("%s %d: else: depth %d\n",m_InputFileName.c_str(),yylineno,m_IndentStack.size()));
+ if (m_IndentStack.top())
+ {
+ throw string("Unexpected else at line ") + stringify(yylineno) + " of file " + m_InputFileName;
+ }
+ m_IndentStack.top()=1;
+ if (m_IndentStack.size()==m_IndentSkip)
+ {
+ BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*if(def|ndef|eq|neq)? {
+ yylineno++;
+ m_IndentStack.push(0);
+ PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),yylineno,yytext+1,m_IndentStack.size()));
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>[a-zA-Z]+ /* skip */
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>[^a-zA-Z\n]+ /* skip */
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*[a-zA-Z]+ yylineno++;
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n {
+ yylineno++;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*#[^\n]* {
+ PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),yylineno));
+ yylineno++;
+ return mhmakeparser::SPACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\.PHONY {
+ PRINTF(("%s %d: .PHONY: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::PHONY;
+}
+
+ /*---------------------------------------------------------------------------*/
+\.AUTODEPS {
+ PRINTF(("%s %d: .AUTODEPS: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::AUTODEPS;
+}
+
+ /*---------------------------------------------------------------------------*/
+export {
+ PRINTF(("%s %d: export: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::EXPORT;
+}
+
+ /*---------------------------------------------------------------------------*/
+^vpath {
+ PRINTF(("%s %d: vpath\n",m_InputFileName.c_str(),yylineno));
+ return mhmakeparser::VPATH;
+}
+
+ /*---------------------------------------------------------------------------*/
+[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+\\[ \t\r]*\n {
+ size_t EndIndex=yyleng;
+ while (strchr(" \t\r\n\\",yytext[--EndIndex]));
+ yyless(EndIndex+1);
+
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+ {
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*\|]|\\\ |\\#)+\\[ \t\r]*\n {
+ size_t EndIndex=yyleng;
+ while (strchr(" \t\r\n\\",yytext[--EndIndex]));
+ yyless(EndIndex+1);
+
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*\|]|\\\ |\\#)+\+= {
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ yyless(yyleng-2);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*\|]|\\\ |\\#)+ {
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+^define[ \t]+[a-zA-Z0-9_\.]+[ \t]*\n {
+ const char *pVar=(const char *)yytext;
+ while (strchr(" \t",*pVar)) pVar++;
+ pVar+=6;
+ theValue.theString=pVar;
+ BEGIN(DEFINE);
+ m_curtoken=g_EmptyString;
+ PRINTF(("%s %d: VARDEF: %s\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str()));
+ yylineno++;
+ return mhmakeparser::VARDEF;
+}
+
+<DEFINE>[ \t]*\\[ \t\r]*\n[ \t]* {
+ yylineno++;
+ m_curtoken+=g_SpaceString;
+}
+
+<DEFINE>. {
+ m_curtoken+=(const char *)yytext;
+}
+
+<DEFINE>[ \t]*\n[ \t]*endef {
+ yylineno++;
+ theValue.theString=m_curtoken;
+ PRINTF(("%s %d: VARVAL: %s\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str()));
+ BEGIN(INITIAL);
+ return mhmakeparser::VARVAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+\" {
+ BEGIN(QUOTE);
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+\' {
+ BEGIN(SINGLEQUOTE);
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\( {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN MACRO $(: %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ BEGIN(MAKEEXPRES);
+ m_curtoken=(const char *)yytext;
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*error[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN ERROR MACRO $(: %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ BEGIN(ERRORMACRO);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE; // Make sure that the previous lines are matched by the bison parser (so that all variables until here are defined)
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*(message|info)[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN MESSAGE MACRO $(: %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ BEGIN(MESSAGEMACRO);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE; // Make sure that the previous lines are matched by the bison parser (so that all variables until here are defined)
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*reparse[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN REPARSE MACRO $(: %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ BEGIN(REPARSEMACRO);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\( {
+ PRINTF(("%s %d: OPENBRACE: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::OPENBRACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\) {
+ PRINTF(("%s %d: CLOSEBRACE: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ return mhmakeparser::CLOSEBRACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\$[<@/$] {
+ PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOLLAREXPR;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t\r]*\n\t[ \t]* {
+ /* token newline */
+ PRINTF(("%s %d: NEWLINE\n",m_InputFileName.c_str(),yylineno));
+ yylineno++;
+ m_curtoken=g_EmptyString;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+<*>\$\{ {
+ unput('(');
+ unput('$');
+}
+ /*---------------------------------------------------------------------------*/
+<*>\} {
+ unput(')');
+}
+
+ /*---------------------------------------------------------------------------*/
+[^\n] {
+ PRINTF(("%s %d: ANYCHAR: %d: %s\n",m_InputFileName.c_str(),yylineno,yylineno,yytext));
+}
+
+ /*****************************************************************************/
+
+<COMMANDPARSE>[ \t\r]*\n {
+ PRINTF(("%s %d: COMMAND: %d: %s\n",m_InputFileName.c_str(),yylineno,yylineno,m_curtoken.c_str()));
+ theValue.theString=m_curtoken;
+ yylineno++;
+ BEGIN(INITIAL);
+ return mhmakeparser::COMMAND;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t\r]*\n\t[ \t]* {
+ PRINTF(("%s %d: COMMAND: %s\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str()));
+ theValue.theString=m_curtoken;
+ yylineno++;
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::COMMAND;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*\\[ \t\r]*\n[ \t]* {
+ yylineno++;
+ m_curtoken+=g_SpaceString;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ ]+ {
+ m_curtoken+=g_SpaceString;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[^ \r\n#\\{}$]+ |
+<COMMANDPARSE>\$ |
+<COMMANDPARSE>\\ {
+ m_curtoken+=(const char *)yytext;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*\\#[^\n]* {
+ int nChars=(int)((strchr((const char *)yytext,'#')-(char*)yytext))+1;
+ yyless(nChars);
+ m_curtoken+=string(yytext,nChars-2);
+ m_curtoken+='#';
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*#[^\n]* {
+ PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+}
+
+ /*****************************************************************************/
+<QUOTE>\" {
+ PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ BEGIN(INITIAL);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+<QUOTE>\r /* skip */
+
+<QUOTE>[^\\\"\r\n{}$]+ |
+<QUOTE>\$ |
+<QUOTE>\\ |
+<QUOTE>\\\" |
+<QUOTE>\\# {
+ yymore();
+}
+
+ /*****************************************************************************/
+<SINGLEQUOTE>\' {
+ PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ BEGIN(INITIAL);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+<SINGLEQUOTE>\r /* skip */
+
+<SINGLEQUOTE>[^\\\'\r\n{}$]+ |
+<SINGLEQUOTE>\$ |
+<SINGLEQUOTE>\\ |
+<SINGLEQUOTE>\\\' |
+<SINGLEQUOTE>\\# {
+ yymore();
+}
+
+ /*****************************************************************************/
+<ERRORMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE ERROR MACRO ): %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: ERRORMACRO: %s\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str()));
+ throw string("\n-> ")+m_InputFileName.c_str()+"("+stringify(yylineno)+") : "+GetParser()->ExpandExpression(m_curtoken);
+ } else {
+ m_curtoken+=(const char *)yytext;
+}
+}
+
+ /*****************************************************************************/
+<MESSAGEMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE MESSAGE MACRO ): %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: MESSAGEMACRO: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ cerr<<m_InputFileName.c_str()<<"("<<stringify(yylineno)+") : "<<GetParser()->ExpandExpression(m_curtoken)<<endl;
+ BEGIN(INITIAL);
+ } else {
+ m_curtoken+=(const char *)yytext;
+ }
+}
+
+ /*****************************************************************************/
+<REPARSEMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE REPARSE MACRO ): %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: REPARSEMACRO: %s\n",m_InputFileName.c_str(),yylineno,yytext));
+ string Deps=GetParser()->ExpandExpression(m_curtoken);
+ PRINTF(("%s %d: REPARSEMACRO expanded: %s\n",m_InputFileName.c_str(),yylineno,Deps.c_str()));
+ string::const_reverse_iterator It=Deps.rbegin()+1; // +1 because we don't want the latest brace
+ string::const_reverse_iterator ItBeg=Deps.rend();
+ while (It!= ItBeg)
+ {
+ char Char=*It++;
+ if (Char==';') Char='\n';
+ unput(Char);
+ }
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ m_curtoken+=(const char *)yytext;
+ }
+}
+
+ /*****************************************************************************/
+<MAKEEXPRES>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE MAKEEXPRES MACRO ): %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ BEGIN(INITIAL);
+ m_curtoken+=(const char *)yytext;
+ theValue.theString=m_curtoken;
+ PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),yylineno,m_curtoken.c_str()));
+ return mhmakeparser::DOLLAREXPR;
+ }
+ else
+ {
+ m_curtoken+=(const char *)yytext;
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$\( {
+ m_BraceIndent++;
+ PRINTF(("%s %d: MACRO extra $(: %d\n",m_InputFileName.c_str(),yylineno,m_BraceIndent));
+ m_curtoken+=(const char *)yytext;
+}
+
+ /*---------------------------------------------------------------------------*/
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>[^$\(\){}\r\n\\]+ |
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$ |
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\\ |
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\( {
+ m_curtoken+=(const char *)yytext;
+}
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>[ \t\r]*\\[ \t\r]*\n[ \t\r]* {
+ yylineno++;
+ m_curtoken+=g_SpaceString;
+}
+
+<SKIPUNTILELSEORENDIF><<EOF>> {
+ throw string("Missing endif or else statement. #else or #endif used?");
+}
+
+<<EOF>> {
+ if (m_BraceIndent)
+ {
+ throw string("Missing closing ) of macro usage in ") + m_InputFileName;
+ }
+ if (!m_IncludeStack.size())
+ {
+ if (m_IndentStack.size())
+ {
+ throw string("Missing endif or else statement in ") + m_InputFileName + ". #else or #endif used";
+ }
+ yyterminate();
+ }
+ else
+ {
+ INSTACK *pInStack=m_IncludeStack.top();
+ yypop_buffer_state();
+ m_InputFileName=pInStack->m_FileName;
+ yylineno=pInStack->yylineno;
+ m_IncludeStack.pop();
+ }
+}
+%%
+
diff --git a/tools/mhmake/src/mhmakeparser.y b/tools/mhmake/src/mhmakeparser.y
new file mode 100644
index 000000000..515caf6e5
--- /dev/null
+++ b/tools/mhmake/src/mhmakeparser.y
@@ -0,0 +1,255 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* -------------- declaration section -------------- */
+
+%name mhmakeparser
+
+%define LLOC m_yyloc
+%define LTYPE int // This is the type of LLOC (defined in mhmakefileparser)
+
+%define LVAL m_theTokenValue
+%define STYPE TOKENVALUE // This is the type of LVAL (defined in mhmakefileparser)
+
+%define INHERIT : public mhmakefileparser
+
+%define CONSTRUCTOR_PARAM const map<string,string> &CommandLineVariables
+%define CONSTRUCTOR_INIT : mhmakefileparser(CommandLineVariables)
+
+%header{
+#include "mhmakefileparser.h"
+#include "rule.h"
+#include "util.h"
+%}
+
+%token <theString> COMMAND
+%token <theString> COMMA OPENBRACE CLOSEBRACE
+%token <theString> STRING DOLLAREXPR EQUAL COLON DOUBLECOLON VARDEF VARVAL
+%token IMEQUAL PEQUAL OPTEQUAL PHONY AUTODEPS EXPORT NEWLINE INCLUDEMAK SPACE VPATH
+
+%type <theString> expression nonspaceexpression simpleexpression
+%type <theString> maybeemptyexpression
+%type <theString> expression_nocolorequal simpleexpression_nocolorequal nonspaceexpression_nocolorequal
+%type <ival> rulecolon
+
+%start file
+
+/* -------------- rules section -------------- */
+/* Sample parser. Does count Chars in a line, and lines in file */
+%%
+file : statements
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetQuotedFullFileName().c_str()));
+ AddRule();
+ }
+ }
+;
+
+statements :
+ | statements statement
+;
+
+statement: NEWLINE |
+ SPACE |
+ includemak |
+ ruledef |
+ phonyrule |
+ autodepsrule |
+ varassignment |
+ imvarassignment |
+ pvarassignment |
+ optvarassignment |
+ exportrule |
+ vpathrule |
+ COMMAND
+ {
+ if (!m_pCurrentRule)
+ {
+ m_pCurrentRule=refptr<rule>(new rule(this));
+ }
+ m_pCurrentRule->AddCommand($1);
+ PRINTF(("Adding command : %s\n",$1.c_str()));
+ }
+;
+
+includemak:
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetQuotedFullFileName().c_str()));
+ AddRule();
+ }
+ } INCLUDEMAK
+;
+
+ruledef: expression_nocolorequal rulecolon maybeemptyexpression
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetQuotedFullFileName().c_str()));
+ AddRule();
+ }
+
+ m_pCurrentItems=new fileinfoarray;
+ m_pCurrentDeps=new fileinfoarray;
+ #ifdef _DEBUG
+ if (!ExpandExpression($1).size())
+ {
+ throw string("Empty left hand side in rule: ") + $1 + " : " + $3;
+ }
+ #endif
+ SplitToItems(ExpandExpression($1),*m_pCurrentItems);
+ SplitToItems(ExpandExpression($3),*m_pCurrentDeps);
+ m_DoubleColonRule= ($2==1) ;
+ PRINTF(("Defining rule %s : %s\n",$1.c_str(),$3.c_str()));
+ PRINTF((" Expanded to %s : %s\n",ExpandExpression($1).c_str(),ExpandExpression($3).c_str()));
+ }
+;
+
+rulecolon: COLON {$$=0;} |
+ DOUBLECOLON {$$=1;}
+;
+
+phonyrule: PHONY COLON expression
+ {
+ vector<fileinfo*> Items;
+ SplitToItems(ExpandExpression($3),Items);
+ vector<fileinfo*>::iterator pIt=Items.begin();
+ while (pIt!=Items.end())
+ {
+ (*pIt)->SetPhony();
+ pIt++;
+ }
+ PRINTF(("Defining phony rule : %s\n",$3.c_str()));
+ PRINTF((" Expanded to : %s\n",ExpandExpression($3).c_str()));
+ }
+ NEWLINE
+;
+
+autodepsrule: AUTODEPS COLON expression
+ {
+ vector<fileinfo*> Items;
+ SplitToItems(ExpandExpression($3),Items);
+ vector<fileinfo*>::iterator pIt=Items.begin();
+ while (pIt!=Items.end())
+ {
+ (*pIt)->SetAutoDepsScan(this);
+ pIt++;
+ }
+ PRINTF(("Defining autodeps rule : %s\n",$3.c_str()));
+ PRINTF((" Expanded to : %s\n",ExpandExpression($3).c_str()));
+ }
+ NEWLINE
+;
+
+exportrule: EXPORT SPACE exportstrings NEWLINE
+;
+
+exportstrings : exportstring |
+ exportstring SPACE exportstrings
+;
+
+exportstring : STRING
+ {
+ SetExport($1,ExpandExpression(ExpandVar($1)));
+ PRINTF(("Exporting %s : %s\n",$1.c_str(),ExpandExpression(ExpandVar($1)).c_str()));
+ }
+;
+
+vpathrule: VPATH SPACE nonspaceexpression SPACE expression NEWLINE
+ {
+ SetvPath(ExpandExpression($3),ExpandExpression($5));
+ PRINTF(("Setting vpath %s to %s\n",$3.c_str(),ExpandExpression($5).c_str()));
+ }
+;
+
+varassignment: VARDEF VARVAL
+ {
+ m_Variables[f_strip($1,NULL)]=$2;
+ PRINTF(("Defining variable %s to %s\n",f_strip($1,NULL).c_str(), $2.c_str()));
+ }
+ | STRING EQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=$3;
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), $3.c_str()));
+ }
+;
+
+imvarassignment: STRING IMEQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=ExpandExpression($3);
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), m_Variables[$1].c_str()));
+ }
+;
+
+pvarassignment: STRING PEQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=m_Variables[$1]+g_SpaceString+$3;
+ PRINTF(("Adding to variable %s: %s\n",$1.c_str(), $3.c_str()));
+ }
+;
+
+optvarassignment: STRING OPTEQUAL maybeemptyexpression
+ {
+ if (!IsDefined($1))
+ {
+ m_Variables[$1]=$3;
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), $3.c_str()));
+ }
+ }
+;
+
+maybeemptyexpression: NEWLINE {$$=g_EmptyString;} |
+ expression NEWLINE |
+ expression SPACE NEWLINE
+;
+
+expression: nonspaceexpression |
+ expression SPACE nonspaceexpression {$$=$1+g_SpaceString+$3;}
+;
+
+expression_nocolorequal: nonspaceexpression_nocolorequal |
+ expression_nocolorequal SPACE nonspaceexpression_nocolorequal {$$=$1+g_SpaceString+$3;}
+;
+
+nonspaceexpression: simpleexpression |
+ nonspaceexpression simpleexpression {$$=$1+$2;}
+;
+
+nonspaceexpression_nocolorequal: simpleexpression_nocolorequal |
+ nonspaceexpression_nocolorequal simpleexpression_nocolorequal {$$=$1+$2;}
+;
+
+simpleexpression: simpleexpression_nocolorequal |
+ EQUAL |
+ COLON |
+ DOUBLECOLON |
+ COMMA
+;
+
+simpleexpression_nocolorequal: STRING {$$=$1;} |
+ DOLLAREXPR {$$=$1;}
+;
+
+%%
+/* -------------- body section -------------- */
+
diff --git a/tools/mhmake/src/refptr.h b/tools/mhmake/src/refptr.h
new file mode 100644
index 000000000..7223090f9
--- /dev/null
+++ b/tools/mhmake/src/refptr.h
@@ -0,0 +1,124 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __REFPTR_H__
+#define __REFPTR_H__
+
+#ifdef _DEBUG
+#define PRINTF(arg) if (g_PrintLexYacc) printf arg
+#else
+#define PRINTF(arg)
+#endif
+
+template <class T> class iterstack: public stack<T>
+{
+ public:
+ typedef typename deque<T>::iterator iterator;
+ typedef typename deque<T>::reverse_iterator reverse_iterator;
+
+ iterator begin()
+ {
+ return stack<T>::c.begin();
+ }
+ iterator end()
+ {
+ return stack<T>::c.end();
+ }
+ reverse_iterator rbegin()
+ {
+ return stack<T>::c.rbegin();
+ }
+ reverse_iterator rend()
+ {
+ return stack<T>::c.rend();
+ }
+};
+
+struct refbase
+{
+ int m_Count;
+ refbase()
+ {
+ m_Count=1;
+ }
+};
+
+// Template class T needs to be derived from refbase;
+template <class T> class refptr
+{
+ T *m_RefPtr;
+public:
+ refptr()
+ {
+ m_RefPtr=NULL;
+ }
+ refptr(T *Ptr) {
+ m_RefPtr=Ptr;
+ }
+ refptr(const refptr<T> &RefPtr)
+ {
+ m_RefPtr=RefPtr.m_RefPtr;
+ if (m_RefPtr)
+ m_RefPtr->m_Count++;
+ }
+ ~refptr()
+ {
+ if (m_RefPtr && !--m_RefPtr->m_Count)
+ {
+ delete m_RefPtr;
+ }
+ }
+ refptr<T> &operator=(const refptr<T>& Src)
+ {
+ if (Src.m_RefPtr!=m_RefPtr)
+ {
+ this->~refptr();
+ new (this) refptr<T>(Src);
+ }
+ return *this;
+ }
+
+ refptr<T> &operator=(T *pPtr) // Assumes that T has reference count 1 and has no other users
+ {
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ if (pPtr && pPtr->m_Count!=1)
+ DebugBreak();
+ #endif
+ this->~refptr();
+ m_RefPtr=pPtr;
+ return *this;
+ }
+
+ T & operator*() const
+ {
+ return *m_RefPtr;
+ }
+ T * operator->() const
+ {
+ return m_RefPtr;
+ }
+ operator T*() const
+ {
+ return m_RefPtr;
+ }
+};
+
+#endif
+
diff --git a/tools/mhmake/src/rule.cpp b/tools/mhmake/src/rule.cpp
new file mode 100644
index 000000000..0beb29c39
--- /dev/null
+++ b/tools/mhmake/src/rule.cpp
@@ -0,0 +1,301 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+refptr<rule> NullRule;
+
+set<rule*> IMPLICITRULE::m_ImplicitRuleRecurseDetStack;
+vector<implicitrule_t> IMPLICITRULE::m_ImplicitRules;
+
+makecommand g_MakeCommand; // Order is important since sm_Statics is using g_MakeCommand
+const string g_QuoteString("\""); // Order is important since sm_Statics is using g_QuoteString
+loadedmakefile::loadedmakefile_statics loadedmakefile::sm_Statics;
+
+///////////////////////////////////////////////////////////////////////////////
+static bool FindDep(fileinfo *pTarget, implicitruledep_t *pImplicitRule,refptr<rule> &Rule)
+{
+ implicitruledep_t::iterator SecIt=pImplicitRule->begin();
+ while (SecIt!=pImplicitRule->end())
+ {
+ if (SecIt->first.empty())
+ {
+ #ifdef _DEBUG
+ // Check if the rule has the same commands
+ vector<string> &OldCommands=SecIt->second->GetCommands();
+ vector<string> &NewCommands=Rule->GetCommands();
+
+ bool bCommandsDifferent=OldCommands.size()!=NewCommands.size();
+ if (g_PrintMultipleDefinedRules || bCommandsDifferent)
+ {
+ string ErrorMessage;
+ if (bCommandsDifferent)
+ ErrorMessage += "Implicit Rule '"+ pTarget->GetFullFileName() + "' defined twice with different commands\n";
+ else
+ ErrorMessage += "Implicit Rule '"+ pTarget->GetFullFileName() + "' defined twice with same commands\n";
+ ErrorMessage += "Command 1: makedir = " + SecIt->second->GetMakefile()->GetMakeDir()->GetQuotedFullFileName()+ "\n";
+
+ vector<string>::const_iterator It;
+ if (bCommandsDifferent)
+ {
+ It=OldCommands.begin();
+ while (It!=OldCommands.end())
+ {
+ ErrorMessage += " " + *It + "\n";
+ }
+ }
+ cerr << "Command 2: makedir = "+ Rule->GetMakefile()->GetMakeDir()->GetQuotedFullFileName()+ "\n";
+ if (bCommandsDifferent)
+ {
+ It=NewCommands.begin();
+ while (It!=NewCommands.end())
+ {
+ ErrorMessage += " " + *It + "\n";
+ }
+ throw ErrorMessage;
+ }
+ else
+ cerr << ErrorMessage << endl;
+ }
+ mhmakefileparser *pOldMakefile=SecIt->second->GetMakefile();
+ mhmakefileparser *pNewMakefile=Rule->GetMakefile();
+ vector<string>::iterator OldIt=OldCommands.begin();
+ vector<string>::iterator NewIt=NewCommands.begin();
+ while (OldIt!=OldCommands.end())
+ {
+ if (pOldMakefile->ExpandExpression(*OldIt)!=pNewMakefile->ExpandExpression(*NewIt))
+ {
+ string ErrorMessage = string("Implicit Rule '") + pTarget->GetFullFileName() + "' defined twice with different commands\n";
+ ErrorMessage += "Command 1: makedir = " + pOldMakefile->GetMakeDir()->GetQuotedFullFileName()+ "\n";
+ ErrorMessage += " " + pOldMakefile->ExpandExpression(*OldIt) + "\n";
+ ErrorMessage += "Command 2: makedir = " + pNewMakefile->GetMakeDir()->GetQuotedFullFileName()+ "\n";
+ ErrorMessage += " " + pNewMakefile->ExpandExpression(*NewIt);
+ throw ErrorMessage;
+ }
+ OldIt++;
+ NewIt++;
+ }
+ #endif
+ return true;
+ }
+ SecIt++;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::AddImplicitRule(fileinfo *pTarget,const vector<fileinfo*> &Deps,refptr<rule> Rule)
+{
+ if (!Rule)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "No commands defined for implicit rule " << pTarget->GetFullFileName()<<endl;
+ #endif
+ return;
+ }
+ // first search if there is already the same target in the current list of implicit rules
+ implicitruledep_t* pImplicitRule=NULL;
+ vector<implicitrule_t>::iterator RuleIt=m_ImplicitRules.begin();
+ while (RuleIt!=m_ImplicitRules.end())
+ {
+ if (pTarget==RuleIt->first)
+ {
+ pImplicitRule=&(RuleIt->second);
+ }
+ RuleIt++;
+ }
+ if (!pImplicitRule)
+ {
+ // Add a new entry
+ m_ImplicitRules.push_back(implicitrule_t(pTarget,implicitruledep_t()));
+ pImplicitRule=&((m_ImplicitRules.end()-1)->second);
+ }
+
+ if (Deps.size())
+ {
+ #ifdef _DEBUG
+ vector<fileinfo*>::const_iterator DepIt=Deps.begin();
+ while (DepIt!=Deps.end())
+ {
+ if (*DepIt==pTarget)
+ throw(string("Implicit rule : ")+pTarget->GetFullFileName()+" is directly dependent on itself. This is not allowed.");
+ DepIt++;
+ }
+ #endif
+ pImplicitRule->push_back(pair<vector<fileinfo*>,refptr<rule> >(Deps,Rule));
+ }
+ else
+ {
+ if (!FindDep(pTarget,pImplicitRule,Rule))
+ pImplicitRule->push_back(pair<vector<fileinfo*>, refptr<rule> >(vector<fileinfo*>(), Rule));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::SearchImplicitRule(const fileinfo *pTarget, implicitruledep_t &Result)
+{
+ string TargetFileName=pTarget->GetFullFileName();
+
+ vector<implicitrule_t>::iterator ImpRegExIt=m_ImplicitRules.begin();
+ while (ImpRegExIt!=m_ImplicitRules.end())
+ {
+ matchres Res;
+
+ if (PercentMatch(TargetFileName,ImpRegExIt->first->GetFullFileName(),&Res))
+ {
+ implicitruledep_t::iterator ResIt=ImpRegExIt->second.begin();
+ while (ResIt!=ImpRegExIt->second.end())
+ {
+#ifdef _DEBUG
+ if (!ResIt->second)
+ {
+ throw string("No commands for implicit rule : ") + ImpRegExIt->first->GetFullFileName();
+ }
+#endif
+ ResIt->second->SetStem(Res.m_Stem);
+ vector<fileinfo*> Deps;
+ const fileinfo *pMakeDir=ResIt->second->GetMakefile()->GetMakeDir();
+ vector<fileinfo*>::iterator It=ResIt->first.begin();
+ while (It!=ResIt->first.end())
+ {
+ string Dependent=ReplaceWithStem((*It)->GetFullFileName(),Res.m_Stem);
+ #ifdef _DEBUG
+ if (Dependent.length()>MAX_PATH)
+ {
+ /* File name is getting too long, this is most probable an infinit loop */
+ throw(string("Filename too long in implicit rule search: ")+Dependent+"\nProbably some infinit loop in the implicit rules search.\n");
+ }
+ #endif
+ Deps.push_back(GetFileInfo(Dependent,pMakeDir));
+ It++;
+ }
+ Result.push_back(pair<vector<fileinfo*>,refptr<rule> >(Deps, ResIt->second));
+ ResIt++;
+ }
+ }
+ ImpRegExIt++;
+ }
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool rule::operator != (const rule &Other)
+{
+ if (m_Commands.size()!=Other.m_Commands.size())
+ return true;
+
+ vector<string>::const_iterator It=m_Commands.begin();
+ vector<string>::const_iterator OtherIt=Other.m_Commands.begin();
+ while (It!=m_Commands.end())
+ {
+ if (m_pMakefile->ExpandExpression(*It)!=Other.m_pMakefile->ExpandExpression(*OtherIt))
+ return true;
+ It++;
+ OtherIt++;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void rule::SetTargetsIsBuild(uint32 Md5_32)
+{
+ vector< fileinfo* >::iterator It=m_Targets.begin();
+ while (It!=m_Targets.end())
+ {
+ (*It)->SetCommandsMd5_32(Md5_32);
+ (*It)->SetBuild();
+ m_pMakefile->AddTarget(*It);
+ It++;
+ }
+}
+///////////////////////////////////////////////////////////////////////////////
+void rule::SetTargetsIsBuilding(const fileinfo *pSrc)
+{
+ vector< fileinfo* >::iterator It=m_Targets.begin();
+ while (It!=m_Targets.end())
+ {
+ if ((*It)!=pSrc)
+ (*It)->SetBuilding(false);
+ It++;
+ }
+}
+
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::PrintImplicitRules()
+{
+ vector<implicitrule_t>::iterator ImpRegExIt=m_ImplicitRules.begin();
+ while (ImpRegExIt!=m_ImplicitRules.end())
+ {
+ implicitruledep_t::iterator SecIt=ImpRegExIt->second.begin();
+ cout << ImpRegExIt->first->GetFullFileName() << " :\n";
+ while (SecIt!=ImpRegExIt->second.end())
+ {
+ cout << " :";
+ vector<fileinfo*>::iterator DepIt=SecIt->first.begin();
+ while (DepIt!=SecIt->first.end())
+ {
+ cout << " " << (*DepIt)->GetQuotedFullFileName() <<endl;
+ DepIt++;
+ }
+ cout << endl;
+ if (SecIt->second)
+ {
+ SecIt->second->PrintCommands();
+ }
+ else
+ {
+ cout << " No rhs\n";
+ }
+ SecIt++;
+ }
+ ImpRegExIt++;
+ }
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void rule::PrintCommands(fileinfo *pTarget) const
+{
+ if (pTarget)
+ m_pMakefile->SetRuleThatIsBuild(pTarget);
+
+ vector<string>::const_iterator pCommandIt=m_Commands.begin();
+ while (pCommandIt!=m_Commands.end())
+ {
+ cout<<g_SpaceString<<*pCommandIt<<endl;
+ if (pTarget)
+ {
+ cout<<" ("<<m_pMakefile->ExpandExpression(*pCommandIt)<<")\n";
+ }
+ pCommandIt++;
+ }
+}
+
+#endif
+
+
diff --git a/tools/mhmake/src/rule.h b/tools/mhmake/src/rule.h
new file mode 100644
index 000000000..7cc287600
--- /dev/null
+++ b/tools/mhmake/src/rule.h
@@ -0,0 +1,110 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __RULE_H__
+#define __RULE_H__
+
+#include "refptr.h"
+#include "md5.h"
+class mhmakefileparser;
+class fileinfo;
+
+class rule: public refbase
+{
+ vector<string> m_Commands;
+ string m_Stem; // Contains the stem in case the rule is part of an implicit rule (filled in in the implicit search)
+ mhmakefileparser* m_pMakefile;
+ vector< fileinfo* > m_Targets; /* Targets that are build with this rule, do not use refptr here because otherwise we get circular references */
+public:
+ rule(mhmakefileparser *pMakefile): m_pMakefile(pMakefile)
+ {
+ }
+
+ void AddCommand(const string &Command)
+ {
+ if (!Command.empty())
+ m_Commands.push_back(Command);
+ }
+ vector<string>& GetCommands()
+ {
+ return m_Commands;
+ }
+ void PrintCommands(fileinfo *pTarget=NULL) const;
+
+ void SetStem(const string &Stem)
+ {
+ m_Stem=Stem;
+ }
+ const string &GetStem() const
+ {
+ return m_Stem;
+ }
+ void SetMakefile(mhmakefileparser *pMakefile)
+ {
+ m_pMakefile=pMakefile;
+ }
+ mhmakefileparser *GetMakefile()
+ {
+ return m_pMakefile;
+ }
+ bool operator != (const rule &Rule);
+
+ void AddTarget(fileinfo *pTarget)
+ {
+ m_Targets.push_back(pTarget);
+ }
+ void SetTargetsIsBuild(uint32 Md5_32);
+ void SetTargetsIsBuilding(const fileinfo *pSrc);
+};
+
+typedef vector<pair<vector<fileinfo*>,refptr<rule> > > implicitruledep_t;
+typedef pair<fileinfo *, implicitruledep_t > implicitrule_t;
+
+class IMPLICITRULE
+{
+ static set<rule*> m_ImplicitRuleRecurseDetStack;
+ static vector<implicitrule_t> m_ImplicitRules; // Use a vector and not a map because the order of the implicit rules is important
+public:
+ static void AddImplicitRule(fileinfo *pTarget,const vector<fileinfo*> &Deps, refptr<rule> pRule);
+ static void SearchImplicitRule(const fileinfo *pTarget, implicitruledep_t &Result);
+ static void PrintImplicitRules();
+ static bool PushRule(rule *pRule)
+ {
+ set<rule*>::iterator pFound=m_ImplicitRuleRecurseDetStack.find(pRule);
+ if (pFound==m_ImplicitRuleRecurseDetStack.end())
+ {
+ m_ImplicitRuleRecurseDetStack.insert(pRule);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ static void PopRule(rule *pRule)
+ {
+ m_ImplicitRuleRecurseDetStack.erase(pRule);
+ }
+};
+
+extern refptr<rule> NullRule;
+
+#endif
+
diff --git a/tools/mhmake/src/stdafx.cpp b/tools/mhmake/src/stdafx.cpp
new file mode 100644
index 000000000..b2168250b
--- /dev/null
+++ b/tools/mhmake/src/stdafx.cpp
@@ -0,0 +1,24 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+// This file is only used to generate the pre-compiled header files. The stdafx.h file
+// should only contain includes of headers that will not be likely to change (like system header files)
+
+#include "stdafx.h"
diff --git a/tools/mhmake/src/stdafx.h b/tools/mhmake/src/stdafx.h
new file mode 100644
index 000000000..5d1ffedfb
--- /dev/null
+++ b/tools/mhmake/src/stdafx.h
@@ -0,0 +1,86 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_)
+#define AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#pragma warning (disable:4786)
+#pragma warning (disable:4503)
+#pragma warning (disable:4530)
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <queue>
+#include <map>
+#include <set>
+#include <stack>
+#include <algorithm>
+#include <sstream>
+
+#ifdef _MSC_VER
+#include <io.h>
+#define __CDECL __cdecl
+#else
+#define __CDECL
+#define _stricmp strcasecmp
+#define _strnicmp strncasecmp
+#define MAX_PATH 255
+#include <sys/wait.h>
+#include <popt.h>
+#include <glob.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+#include <string.h>
+#ifdef _MSC_VER
+#include <direct.h>
+#define mkdir(name,mode) _mkdir(name)
+#endif
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+using namespace std;
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_)
+
diff --git a/tools/mhmake/src/util.cpp b/tools/mhmake/src/util.cpp
new file mode 100644
index 000000000..63bdb767b
--- /dev/null
+++ b/tools/mhmake/src/util.cpp
@@ -0,0 +1,780 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+#ifdef WIN32
+
+#include <WinIoCtl.h>
+
+#define REPARSE_MOUNTPOINT_HEADER_SIZE 8
+
+typedef struct {
+ DWORD ReparseTag;
+ DWORD ReparseDataLength;
+ WORD Reserved;
+ WORD ReparseTargetLength;
+ WORD ReparseTargetMaximumLength;
+ WORD Reserved1;
+ WCHAR ReparseTarget[1];
+} REPARSE_MOUNTPOINT_DATA_BUFFER, *PREPARSE_MOUNTPOINT_DATA_BUFFER;
+#endif
+
+static char s_UsageString[]=
+"\
+Usage: mhmake [-f <Makefile>] [-[c|C] <RunDir>] [<Var>=<Value>]\n\
+ [-a] [-q] [-s] [-v] [-P <Nr Parallel Builds>] [targets]+\n"
+#ifdef _DEBUG
+"\
+ [-p] [-n] [-e] [-l] [-w] [-d] [-CD] [-m] [-b]\n"
+#endif
+"\n\
+ <Makefile> : Makefile to load (if not specified 'makefile' is used\n\
+ <RunDir> : Make is setting the current directory to this directory at the\n\
+ start.\n\
+ <Var> : Defines a variable\n\
+ <Value> : Value of the variable\n\
+ -a : Rebuild all targets\n\
+ -s : Rescan automatic dependencies\n\
+ -v : Print version information\n\
+ -q : Quiet. Disable all output \n\
+ -P <Nr Parallel Builds> :\n\
+ Number of parallel build commands executed at the \n\
+ same time. Default is this the number of processor \n\
+ cores. 1 disables parallel builds.\n"
+#ifdef _DEBUG
+"\n\
+ The following options are additional options in mhmake_dbg which are not\n\
+ available in mhmake. These are mainly options for debugging purposes.\n\
+ -e : Dump Vars and Rules on error\n\
+ -w : Print additional information\n\
+ -p : Prints the variables and the rules before building\n\
+ -n : Only prints the commands, but does not execute them\n\
+ -l : Print parser debug information\n\
+ -d : Print the dependency checking\n\
+ -CD : Do circular dependency checking (targets depending on itself)\n\
+ -m : Create md5 database in md5.database in start directory. \n\
+ -b : Print build tree. \n\
+ -D : Print all double defined rules (even if commands are the same) \n\
+"
+#else
+"\
+\n\
+It is adviced during creation of makefiles to use mhmake_dbg. It has additional\n\
+debugging options and does some extra error checking.\n\
+For a description of the additional options: run mhmake_dbg -h\n\
+"
+#endif
+;
+
+#ifdef _DEBUG
+bool g_PrintVarsAndRules=false;
+bool g_DoNotExecute=false;
+bool g_BuildMd5Db=false;
+bool g_GenProjectTree=false;
+bool g_DumpOnError=false;
+bool g_PrintAdditionalInfo=false;
+bool g_pPrintDependencyCheck=false;
+bool g_CheckCircularDeps=false;
+bool g_PrintLexYacc=false;
+bool g_PrintMultipleDefinedRules=false;
+#endif
+
+bool g_Quiet=false;
+bool g_RebuildAll=false;
+bool g_ForceAutoDepRescan=false;
+
+const string g_EmptyString;
+const string g_SpaceString(" ");
+
+///////////////////////////////////////////////////////////////////////////////
+void PrintVersionInfo(void)
+{
+ static const char VersionStr[]="\
+mhmake : GNU compatible make tool with extensions\n\
+version: "MHMAKEVER"\n\
+Remarks and bug reports -> marha@sourceforge.net\n\
+";
+ cerr << VersionStr;
+ exit(1);
+}
+///////////////////////////////////////////////////////////////////////////////
+makecommand::makecommand()
+{
+ char ExeName[MAX_PATH];
+#ifdef WIN32
+ GetModuleFileName(NULL,ExeName,sizeof(ExeName));
+ m_BuildCommand=ExeName;
+ transform(m_BuildCommand.begin(),m_BuildCommand.end(),m_BuildCommand.begin(),(int(__CDECL *)(int))tolower);
+#else
+ int NrChars=readlink ("/proc/self/exe", ExeName, sizeof(ExeName));
+ ExeName[NrChars]=0;
+ m_BuildCommand=ExeName;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string Substitute(const string &ToSubst,const string &iSrcStr,const string &iToStr)
+{
+ string Ret=g_EmptyString;
+ matchres Res;
+ string SrcStr=iSrcStr;
+ string ToStr=iToStr;
+
+ if (string::npos==SrcStr.find('%'))
+ {
+ string PerStr("%");
+ SrcStr=PerStr+SrcStr;
+ ToStr=PerStr+ToStr;
+ }
+ const char *pTmp=ToSubst.c_str();
+ bool first=true;
+ while (*pTmp)
+ {
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+
+ if (PercentMatch(Item,SrcStr,&Res))
+ {
+ Ret+=ReplaceWithStem(ToStr,Res.m_Stem);
+ }
+ else
+ {
+ Ret+=Item;
+ }
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool PercentMatch(const string &String,const string &Expr,matchres *pRes,const char Char)
+{
+ const char *pFirst=String.c_str();
+ const char *pFirstExpr=Expr.c_str();
+ while (*pFirstExpr && *pFirstExpr!=Char)
+ {
+ if (*pFirst!=*pFirstExpr)
+ return false;
+ pFirst++;
+ pFirstExpr++;
+ }
+
+ if (!*pFirstExpr)
+ {
+ if (!*pFirst)
+ {
+ if (pRes)
+ {
+ pRes->m_First=String;
+ pRes->m_Stem=pRes->m_Last=g_EmptyString;
+ }
+ return true;
+ } else
+ return false;
+ }
+ else if (!*pFirst)
+ return false;
+
+ const char *pEnd=pFirst+strlen(pFirst);
+
+ const char *pLast=pEnd;
+ const char *pLastExpr=pFirstExpr+strlen(pFirstExpr)-1;
+ if (pLastExpr!=pFirstExpr)
+ {
+ pLast--;
+
+ while (pLastExpr>pFirstExpr)
+ {
+ if (*pLastExpr!=*pLast)
+ return false;
+ pLastExpr--;
+ pLast--;
+ }
+ pLast++;
+ }
+ string Stem=string(pFirst,pLast-pFirst);
+
+ if (pRes)
+ {
+ pRes->m_First=string(String.c_str(),pFirst-String.c_str());
+
+ pRes->m_Stem=Stem;
+
+ pRes->m_Last=string(pLast,pEnd-pLast);
+ }
+ return true;
+}
+///////////////////////////////////////////////////////////////////////////////
+bool PercentMatchNoCase(const string &String,const string &Expr,matchres *pRes,const char Char)
+{
+ const char *pFirst=String.c_str();
+ const char *pFirstExpr=Expr.c_str();
+ while (*pFirstExpr && *pFirstExpr!=Char)
+ {
+ if (tolower(*pFirst)!=tolower(*pFirstExpr))
+ return false;
+ pFirst++;
+ pFirstExpr++;
+ }
+
+ if (!*pFirstExpr)
+ {
+ if (!*pFirst)
+ {
+ if (pRes)
+ {
+ pRes->m_First=String;
+ pRes->m_Stem=pRes->m_Last=g_EmptyString;
+ }
+ return true;
+ } else
+ return false;
+ }
+ else if (!*pFirst)
+ return false;
+
+ const char *pEnd=pFirst+strlen(pFirst);
+
+ const char *pLast=pEnd;
+ const char *pLastExpr=pFirstExpr+strlen(pFirstExpr)-1;
+ if (pLastExpr!=pFirstExpr)
+ {
+ pLast--;
+
+ while (pLastExpr>pFirstExpr)
+ {
+ if (tolower(*pLastExpr)!=tolower(*pLast))
+ return false;
+ pLastExpr--;
+ pLast--;
+ }
+ pLast++;
+ }
+ string Stem=string(pFirst,pLast-pFirst);
+
+ if (pRes)
+ {
+ pRes->m_First=string(String.c_str(),pFirst-String.c_str());
+
+ pRes->m_Stem=Stem;
+
+ pRes->m_Last=string(pLast,pEnd-pLast);
+ }
+ return true;
+}
+///////////////////////////////////////////////////////////////////////////////
+bool PercentMatchList(const string &String,const string &ExprList,matchres *pRes)
+{
+ const char *pTmp=ExprList.c_str();
+ while (*pTmp)
+ {
+ string Expr;
+ pTmp=NextItem(pTmp,Expr);
+ if (PercentMatch(String,Expr,pRes))
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string ReplaceWithStem(const string &String,const string &Stem)
+{
+ string Ret=String;
+ size_t Pos=Ret.find('%');
+ while (Pos!=string::npos)
+ {
+ Ret=Ret.substr(0,Pos)+Stem+Ret.substr(Pos+1);
+ Pos=Ret.find('%');
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+refptr<loadedmakefile> LOADEDMAKEFILES::find(const loadedmakefile &ToSearch)
+{
+ vector<refptr<loadedmakefile> >::const_iterator It=begin();
+ while (It!=end())
+ {
+ if (*(*It)==ToSearch)
+ return *It;
+ It++;
+ }
+ return refptr<loadedmakefile>();
+}
+
+LOADEDMAKEFILES g_LoadedMakefiles;
+
+///////////////////////////////////////////////////////////////////////////////
+loadedmakefile::loadedmakefile_statics::loadedmakefile_statics()
+{
+ m_GlobalCommandLineVars[MAKE]=g_MakeCommand;
+ const char *pEnv=getenv(MHMAKECONF);
+ if (pEnv)
+ {
+ string Env(QuoteFileName(pEnv));
+ m_GlobalCommandLineVars[MHMAKECONF]=Env;
+ m_MhMakeConf=GetAbsFileInfo(Env);
+
+ // Get the revision of the working copy
+ // We do it with the svn info command
+
+ string Output;
+ try
+ {
+ mhmakefileparser Dummy(curdir::GetCurDir());
+ string SvnCommand=Dummy.SearchCommand("svn",EXEEXT);
+ #ifdef WIN32
+ if (GetFileAttributes(m_MhMakeConf->GetFullFileName().c_str())&FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(m_MhMakeConf->GetFullFileName().c_str(),&FindData);
+ if (hFind!=INVALID_HANDLE_VALUE)
+ {
+ if (FindData.dwReserved0==IO_REPARSE_TAG_MOUNT_POINT)
+ {
+ HANDLE hDir = ::CreateFile(m_MhMakeConf->GetFullFileName().c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; // We need a large buffer
+ REPARSE_MOUNTPOINT_DATA_BUFFER& ReparseBuffer = (REPARSE_MOUNTPOINT_DATA_BUFFER&)buf;
+ DWORD dwRet;
+
+ if (::DeviceIoControl(hDir, FSCTL_GET_REPARSE_POINT, NULL, 0, &ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwRet, NULL))
+ {
+ // Success
+ ::CloseHandle(hDir);
+
+ LPCWSTR pPath = ReparseBuffer.ReparseTarget;
+ if (wcsncmp(pPath, L"\\??\\", 4) == 0) pPath += 4; // Skip 'non-parsed' prefix
+ char szPath[MAX_PATH];
+ ::WideCharToMultiByte(CP_ACP, 0, pPath, -1, szPath, MAX_PATH, NULL, NULL);
+ Dummy.OsExeCommand(SvnCommand,string(" info ")+GetFileInfo(szPath,m_MhMakeConf->GetDir())->GetQuotedFullFileName(),false,&Output);
+ }
+ else
+ { // Error
+ ::CloseHandle(hDir);
+ }
+ }
+ FindClose(hFind);
+ }
+ }
+ #else
+ struct stat Stat;
+ lstat(m_MhMakeConf->GetFullFileName().c_str(),&Stat);
+ if (S_ISLNK(Stat.st_mode))
+ {
+ char FileName[1024];
+ int len=readlink(m_MhMakeConf->GetFullFileName().c_str(),FileName,sizeof(FileName));
+ FileName[len]=0;
+ Dummy.OsExeCommand(SvnCommand,string(" info ")+GetFileInfo(FileName,m_MhMakeConf->GetDir())->GetQuotedFullFileName(),false,&Output);
+ }
+ #endif
+ else
+ Dummy.OsExeCommand(SvnCommand,string(" info ")+m_MhMakeConf->GetQuotedFullFileName(),false,&Output);
+ }
+ catch (string Message)
+ {
+ }
+
+ char *pTok=strtok((char*)Output.c_str(),"\n"); // doing this is changing string, so this is very dangerous !!!
+ while (pTok)
+ {
+ if (!strncmp(pTok,"URL: ",5))
+ {
+ m_GlobalCommandLineVars[WC_URL]=pTok+5+7;
+ }
+ else if (!strncmp(pTok,"Revision: ",10))
+ {
+ m_GlobalCommandLineVars[WC_REVISION]=pTok+10;
+ break;
+ }
+ pTok=strtok(NULL,"\n");
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+loadedmakefile::loadedmakefile(const fileinfo *pDir, vector<string> &Args,const string&Makefile)
+{
+ m_Makefile=NULL;
+ m_CommandLineVars=sm_Statics.m_GlobalCommandLineVars;
+
+ m_MakeDir=NULL;
+ vector<string>::iterator ArgIt=Args.begin();
+ while (ArgIt!=Args.end())
+ {
+ size_t EqPos=ArgIt->find('=');
+ if (EqPos!=string::npos)
+ {
+ string Var=ArgIt->substr(0,EqPos);
+ string Val=ArgIt->substr(EqPos+1);
+ m_CommandLineVars[Var]=Val;
+ }
+ else if ((*ArgIt)[0]=='-')
+ {
+ switch ((*ArgIt)[1])
+ {
+ case 'f':
+ if (ArgIt->size()>2)
+ {
+ if (!m_MakeDir)
+ {
+ m_Makefile=GetFileInfo(ArgIt->substr(2),pDir);
+ }
+ else
+ {
+ m_Makefile=GetFileInfo(ArgIt->substr(2),m_MakeDir);
+ }
+ }
+ else
+ {
+ ArgIt++;
+ if (!m_MakeDir)
+ {
+ m_Makefile=GetFileInfo(*ArgIt,pDir);
+ }
+ else
+ {
+ m_Makefile=GetFileInfo(*ArgIt,m_MakeDir);
+ }
+ }
+ break;
+ case 'C':
+#ifdef _DEBUG
+ if (ArgIt->size()>2 && (*ArgIt)[2]=='D')
+ {
+ g_CheckCircularDeps=true;
+ break;
+ }
+#endif
+ /* Fall through */
+ case 'c':
+ if (ArgIt->size()>2)
+ m_MakeDir=GetFileInfo(ArgIt->substr(2),pDir);
+ else
+ {
+ ArgIt++;
+ m_MakeDir=GetFileInfo(*ArgIt,pDir);
+ }
+ break;
+ case 'a':
+ g_RebuildAll=true;
+ break;
+ case 'q':
+ g_Quiet=true;
+ break;
+ case 's':
+ g_ForceAutoDepRescan=true;
+ break;
+ case 'v':
+ PrintVersionInfo();
+ break;
+ case 'P':
+ if (ArgIt->size()>2)
+ mhmakefileparser::SetNrParallelBuilds(atoi(ArgIt->substr(2).c_str()));
+ else
+ {
+ ArgIt++;
+ mhmakefileparser::SetNrParallelBuilds(atoi((*ArgIt).c_str()));
+ }
+ break;
+#ifdef _DEBUG
+ case 'p':
+ g_PrintVarsAndRules=true;
+ break;
+ case 'n':
+ g_DoNotExecute=true;
+ break;
+ case 'w':
+ g_PrintAdditionalInfo=true;
+ break;
+ case 'd':
+ g_pPrintDependencyCheck=true;
+ break;
+ case 'D':
+ g_PrintMultipleDefinedRules=true;
+ break;
+ case 'l':
+ g_PrintLexYacc=true;
+ break;
+ case 'e':
+ g_DumpOnError=true;
+ break;
+ case 'm':
+ g_BuildMd5Db=true;
+ break;
+ case 'b':
+ g_GenProjectTree=true;
+ break;
+#endif
+ default:
+ throw string("\nUnknown option: ")+*ArgIt+"\n\n"+ s_UsageString;
+ }
+ }
+ else
+ {
+ m_CommandLineTargets.push_back(*ArgIt);
+ }
+ ArgIt++;
+ }
+ if (!m_Makefile)
+ {
+ if (!Makefile.empty())
+ {
+ if (!m_MakeDir)
+ m_Makefile=GetFileInfo(Makefile,pDir);
+ else
+ m_Makefile=GetFileInfo(Makefile,m_MakeDir);
+ }
+ }
+ if (!m_Makefile)
+ {
+ if (!m_MakeDir)
+ m_Makefile=GetFileInfo(m_CommandLineTargets[0],pDir);
+ else
+ m_Makefile=GetFileInfo(m_CommandLineTargets[0],m_MakeDir);
+
+ m_CommandLineTargets.erase(m_CommandLineTargets.begin());
+ }
+ if (!m_MakeDir)
+ {
+ if (Makefile==g_EmptyString)
+ m_MakeDir=m_Makefile->GetDir(); /* This is one from load_makefile, so we take the directory of the makefile itself. */
+ else
+ m_MakeDir=curdir::GetCurDir(); /* This means that this is the main makefile given on the command line, so we take the current directory */
+ }
+
+ if (loadedmakefile::sm_Statics.m_MhMakeConf)
+ {
+ const string &RootDir=loadedmakefile::sm_Statics.m_MhMakeConf->GetFullFileName();
+ string MakeDir=m_MakeDir->GetFullFileName();
+ if (RootDir.length()>MakeDir.length() || _strnicmp(RootDir.c_str(),MakeDir.c_str(),RootDir.length()))
+ {
+ cerr<<"mhmake needs to run in a directory that is a subdirectory of the directory specified with %MHMAKECONF : "<<RootDir<<", make dir : "<<m_MakeDir->GetFullFileName()<<endl;
+ exit(1);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void loadedmakefile::LoadMakefile()
+{
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Loading makefile "<<m_Makefile->GetQuotedFullFileName()<<endl;
+ #endif
+
+ m_pParser=refptr<mhmakefileparser>(new mhmakeparser(m_CommandLineVars));
+
+ // Add the MAKECMDGOALS environment variable
+ string MakeCmdGoals;
+ bool First=true;
+ vector<string>::iterator TarIt=m_CommandLineTargets.begin();
+ while (TarIt!=m_CommandLineTargets.end())
+ {
+ if (First)
+ First=false;
+ else
+ MakeCmdGoals+=g_SpaceString;
+ MakeCmdGoals+=*TarIt;
+ TarIt++;
+ }
+ m_pParser->SetVariable("MAKECMDGOALS",MakeCmdGoals);
+
+ string BaseAutoMak;
+
+ // First parse the makefile.before makefile which is in the directory $(MHMAKECONF) environment variable
+ fileinfo *pDepFile;
+ if (sm_Statics.m_MhMakeConf)
+ {
+ BaseAutoMak=m_pParser->ExpandVar(BASEAUTOMAK);
+ if (BaseAutoMak.empty())
+ {
+ BaseAutoMak="makefile";
+ m_pParser->SetVariable(BASEAUTOMAK,BaseAutoMak);
+ }
+ const fileinfo *pBeforeMakefile=GetFileInfo(BaseAutoMak+".before",sm_Statics.m_MhMakeConf);
+
+ int result=m_pParser->ParseFile(pBeforeMakefile,m_MakeDir);
+ if (result)
+ {
+ throw string("Error parsing ")+pBeforeMakefile->GetQuotedFullFileName();
+ }
+ m_pParser->UpdateDate(pBeforeMakefile->GetDate());
+
+ // Now parse the automaticly generated dependency file, which needs to be in the objdir directory
+ string ObjDirName=m_pParser->ExpandExpression("$(OBJDIR)");
+ if (!ObjDirName.size())
+ {
+ throw string("When making use of MHMAKECONF, you have to define OBJDIR in makefile.before");
+ }
+ pDepFile=GetFileInfo(ObjDirName+OSPATHSEPSTR "." + m_Makefile->GetName()+ ".dep",m_MakeDir);
+ m_pParser->SetVariable(AUTODEPFILE,pDepFile->GetQuotedFullFileName());
+ }
+ else
+ {
+ /* Create a file that is depending on the makefile name and the arguments */
+ md5_context ctx;
+
+ md5_starts( &ctx );
+
+ map<string,string>::const_iterator pIt=m_CommandLineVars.begin();
+ while (pIt!=m_CommandLineVars.end())
+ {
+ if (pIt->first!="MAKE")
+ {
+ md5_update(&ctx, (uint8*)pIt->first.c_str(), pIt->first.size());
+ md5_update(&ctx, (uint8*)pIt->second.c_str(), pIt->second.size());
+ }
+ pIt++;
+ }
+ md5_update(&ctx, (uint8*)m_Makefile->GetFullFileName().c_str(), m_Makefile->GetFullFileName().size());
+
+ char ID[10];
+ sprintf(ID,"_%x",md5_finish32( &ctx));
+
+ pDepFile=GetFileInfo(string(".") + m_Makefile->GetName()+ ".dep"+ID,m_MakeDir);
+ m_pParser->SetVariable(AUTODEPFILE,pDepFile->GetQuotedFullFileName());
+ }
+
+ if (pDepFile->Exists())
+ m_pParser->LoadAutoDepsFile(pDepFile); /* Already load this autodep file before parsing of the makefile to avoid needless rebuilds. */
+
+ //m_pParser->yydebug=1;
+ int result=m_pParser->ParseFile(m_Makefile,m_MakeDir);
+ if (result)
+ {
+ throw string("Error parsing ")+m_Makefile->GetQuotedFullFileName();
+ }
+ #ifdef _DEBUG
+ /* Check if the makefile has changed the AUTODEPFILE variable, if so generate a warning that a
+ * rebuild could happen for the rules defined for making included makefiles */
+ if (m_pParser->ExpandVar(AUTODEPFILE)!=pDepFile->GetQuotedFullFileName())
+ {
+ cout << "\n\nWARNING:\n makefile '"<< m_Makefile->GetQuotedFullFileName() <<"' re-defines AUTODEPFILE\n from '"<< pDepFile->GetQuotedFullFileName() <<"'\n to '"<<
+ m_pParser->ExpandVar(AUTODEPFILE) << "'\n (may cause needless rebuilds when having rules for included makefiles!!!!!)\n\n\n";
+ }
+
+ if (g_PrintAdditionalInfo)
+ {
+ if (m_pParser->GetFirstTarget())
+ cout<<"First target of "<<m_Makefile->GetQuotedFullFileName()<<" is "<<m_pParser->GetFirstTarget()->GetQuotedFullFileName()<<endl;
+ else
+ cout<<"No First target for "<<m_Makefile->GetQuotedFullFileName()<<endl;
+ }
+ #endif
+ m_pParser->UpdateDate(m_Makefile->GetDate());
+
+ if (sm_Statics.m_MhMakeConf)
+ {
+ fileinfo *pAfterMakefile=GetFileInfo(BaseAutoMak+".after",sm_Statics.m_MhMakeConf);
+ int result=m_pParser->ParseFile(pAfterMakefile);
+ if (result) {
+ throw string("Error parsing ")+pAfterMakefile->GetQuotedFullFileName();
+ }
+ m_pParser->UpdateDate(pAfterMakefile->GetDate());
+ }
+ bool ForceAutoDepRescan=g_ForceAutoDepRescan;
+ if (pDepFile->Exists())
+ m_pParser->LoadAutoDepsFile(pDepFile);
+ else
+ ForceAutoDepRescan=true;
+ if (ForceAutoDepRescan)
+ m_pParser->EnableAutoDepRescan();
+
+ vector<string> &MakefilesToLoad=m_pParser->GetMakefilesToLoad();
+ vector<string>::iterator It=MakefilesToLoad.begin();
+ while (It!=MakefilesToLoad.end())
+ {
+ // First split the command into arguments
+ const char *pTmp=It->c_str();
+ vector<string> Args;
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ Args.push_back(Item);
+ }
+
+ refptr<loadedmakefile> pLoadedMakefile(new loadedmakefile(m_MakeDir,Args));
+ refptr<loadedmakefile> Found=g_LoadedMakefiles.find(*pLoadedMakefile);
+ if (Found)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Makefile already loaded: "<<Found->m_Makefile->GetQuotedFullFileName()<<endl;
+ #endif
+ }
+ else
+ {
+ g_LoadedMakefiles.push_back(pLoadedMakefile);
+
+ /* If there is a rule to build the makefile, first check if it needs to be rebuild */
+ m_pParser->BuildTarget(pLoadedMakefile->m_Makefile);
+ pLoadedMakefile->LoadMakefile();
+ }
+ It++;
+ }
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void DumpVarsAndRules()
+{
+ int i;
+ LOADEDMAKEFILES::iterator LoadMakIt=g_LoadedMakefiles.begin();
+ while (LoadMakIt!=g_LoadedMakefiles.end())
+ {
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "Variables of makefile " << (*LoadMakIt)->m_Makefile->GetQuotedFullFileName() << endl;
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ (*LoadMakIt)->m_pParser->PrintVariables(true);
+ cout << endl;
+ LoadMakIt++;
+ }
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "All Rules\n";
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ PrintFileInfos();
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "All Implicit Rules\n";
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ IMPLICITRULE::PrintImplicitRules();
+}
+#endif
+
diff --git a/tools/mhmake/src/util.h b/tools/mhmake/src/util.h
new file mode 100644
index 000000000..8676bd0d6
--- /dev/null
+++ b/tools/mhmake/src/util.h
@@ -0,0 +1,238 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2010 marha@sourceforge.net
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include "fileinfo.h"
+
+// List of pre-defined variables
+#define WC_REVISION "WC_REVISION"
+#define WC_URL "WC_URL"
+#define AUTODEPFILE "AUTODEPFILE"
+#define OBJEXTVAR "OBJEXT"
+#define EXEEXTVAR "EXEEXT"
+#define SKIPHEADERS "SKIPHEADERS"
+#define MAKE "MAKE"
+#define MHMAKECONF "MHMAKECONF"
+#define BASEAUTOMAK "BASEAUTOMAK"
+#define CURDIR "CURDIR"
+#define USED_ENVVARS "USED_ENVVARS"
+#define PATH "PATH"
+#ifdef WIN32
+#define COMSPEC "COMSPEC"
+#define PYTHONEXE "python.exe"
+#define EXEEXT ".exe"
+#define OBJEXT ".obj"
+#define PLATFORM "win32"
+#else
+#define COMSPEC "SHELL"
+#define PYTHONEXE "python"
+#define EXEEXT ""
+#define OBJEXT ".o"
+#define PLATFORM "linux"
+#endif
+
+#define MHMAKEVER "2.3.17"
+
+class makecommand
+{
+ string m_BuildCommand;
+public:
+ makecommand();
+ operator string()
+ {
+ return m_BuildCommand;
+ }
+};
+
+extern makecommand g_MakeCommand;
+
+///////////////////////////////////////////////////////////////////////////////
+inline const char *NextItem(const char *pTmp,string &Output, const char *pToks=" \t")
+{
+ const char *pStart;
+ const char *pStop;
+ while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
+ pStart=pTmp;
+ while (1)
+ {
+ if (*pTmp=='"')
+ {
+ pTmp++;
+ while (*pTmp && *pTmp!='"') pTmp++;
+ if (*pTmp) pTmp++;
+ pStop=pTmp;
+ if (!*pTmp || strchr(pToks,*pTmp))
+ break;
+ }
+ else if (*pTmp=='\'')
+ {
+ pTmp++;
+ while (*pTmp && *pTmp!='\'') pTmp++;
+ if (*pTmp) pTmp++;
+ pStop=pTmp;
+ if (!*pTmp || strchr(pToks,*pTmp))
+ break;
+ }
+ else if (!*pTmp)
+ {
+ pStop=pTmp;
+ break;
+ }
+ else
+ {
+ pTmp++;
+ #if OSPATHSEP=='/'
+ while (*pTmp)
+ {
+ if (!strchr(pToks,*pTmp) || (*(pTmp-1)=='\\'))
+ pTmp++;
+ else
+ break;
+ }
+ #else
+ while (!strchr(pToks,*pTmp)) pTmp++;
+ #endif
+ pStop=pTmp;
+ break;
+ }
+ }
+ Output=string(pStart,pStop);
+ // skip trailing space
+ while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
+ return pTmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+inline const char *NextCharItem(const char *pTmp,string &Output,char Char)
+{
+ const char *pStart=pTmp;
+ while (*pTmp && *pTmp!=Char) pTmp++;
+ const char *pStop=pTmp;
+ if (*pTmp) pTmp++;
+
+ while (pStart<pStop && (*pStart==' ' || *pStart == '\t')) pStart++;
+ pStop--;
+ while (pStart<=pStop && (*pStop==' ' || *pStop == '\t')) pStop--;
+ pStop++;
+
+ Output=string(pStart,pStop);
+ return pTmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string Substitute(const string &ToSubst,const string &SrcStr,const string &ToStr);
+
+struct matchres
+{
+ string m_First;
+ string m_Stem;
+ string m_Last;
+};
+
+bool PercentMatch(const string &String,const string &Expr,matchres *pRes=NULL,const char Char='%');
+bool PercentMatchNoCase(const string &String,const string &Expr,matchres *pRes=NULL,const char Char='%');
+bool PercentMatchList(const string &String,const string &ExprList,matchres *pRes=NULL);
+string ReplaceWithStem(const string &String,const string &Stem);
+
+struct loadedmakefile : public refbase
+{
+ struct loadedmakefile_statics
+ {
+ map<string,string> m_GlobalCommandLineVars;
+ fileinfo *m_MhMakeConf;
+
+ loadedmakefile_statics();
+ };
+ static loadedmakefile_statics sm_Statics;
+
+ fileinfo *m_Makefile;
+ const fileinfo *m_MakeDir;
+ map<string,string> m_CommandLineVars;
+
+ vector<string> m_CommandLineTargets;
+ refptr<mhmakefileparser> m_pParser;
+
+ loadedmakefile(const fileinfo *pDir, vector<string> &Args,const string &Makefile=g_EmptyString);
+
+ void LoadMakefile();
+ void AddCommandLineVarsToEnvironment()
+ {
+ map<string,string>::const_iterator It=m_CommandLineVars.begin();
+ map<string,string>::const_iterator ItEnd=m_CommandLineVars.end();
+ while (It!=ItEnd)
+ {
+ sm_Statics.m_GlobalCommandLineVars.insert(*It++);
+ }
+ }
+
+ int operator==(const loadedmakefile &Other)
+ {
+ if (m_Makefile!=Other.m_Makefile)
+ return 0;
+ if (m_MakeDir!=Other.m_MakeDir)
+ return 0;
+ if (m_CommandLineTargets.size()!=Other.m_CommandLineTargets.size())
+ return 0;
+ if (m_CommandLineVars.size()!=Other.m_CommandLineVars.size())
+ return 0;
+ map<string,string>::iterator VarIt=m_CommandLineVars.begin();
+ while (VarIt!=m_CommandLineVars.end())
+ {
+ map<string,string>::const_iterator pFound=Other.m_CommandLineVars.find(VarIt->first);
+ if (pFound==Other.m_CommandLineVars.end())
+ return 0;
+ if (pFound->second!=VarIt->second)
+ return 0;
+ VarIt++;
+ }
+ vector<string>::iterator TarIt=m_CommandLineTargets.begin();
+ while (TarIt!=m_CommandLineTargets.end())
+ {
+ vector<string>::const_iterator OtherIt=Other.m_CommandLineTargets.begin();
+ while (OtherIt!=Other.m_CommandLineTargets.begin())
+ {
+ if (*TarIt==*OtherIt)
+ break;
+ OtherIt++;
+ }
+ if (OtherIt==Other.m_CommandLineTargets.end())
+ return 0;
+ TarIt++;
+ }
+ return 1;
+ }
+};
+
+class LOADEDMAKEFILES : public vector<refptr<loadedmakefile> >
+{
+public:
+ refptr<loadedmakefile> find(const loadedmakefile &pToSearch);
+ typedef vector<refptr<loadedmakefile> >::iterator iterator;
+};
+
+extern LOADEDMAKEFILES g_LoadedMakefiles;
+
+void DumpVarsAndRules();
+
+#endif
+