tizen 2.3.1 release
[external/ragel.git] / ragel / main.cpp
index 0943eef..2b54b51 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ *  Copyright 2001-2007 Adrian Thurston <thurston@complang.org>
  */
 
 /*  This file is part of Ragel.
 #include <fcntl.h>
 #include <errno.h>
 
-#ifndef WIN32
-#include <sys/wait.h>
-#else
+#ifdef _WIN32
 #include <windows.h>
 #include <psapi.h>
+#include <time.h>
+#include <io.h>
+#include <process.h>
+
+#if _MSC_VER
+#define S_IRUSR _S_IREAD
+#define S_IWUSR _S_IWRITE
+#endif
 #endif
 
 /* Parsing. */
@@ -48,6 +54,7 @@
 #include "vector.h"
 #include "version.h"
 #include "common.h"
+#include "inputdata.h"
 
 using std::istream;
 using std::ostream;
@@ -65,16 +72,24 @@ MinimizeLevel minimizeLevel = MinimizePartition2;
 MinimizeOpt minimizeOpt = MinimizeMostOps;
 
 /* Graphviz dot file generation. */
-char *machineSpec = 0, *machineName = 0;
+const char *machineSpec = 0, *machineName = 0;
 bool machineSpecFound = false;
+bool wantDupsRemoved = true;
 
 bool printStatistics = false;
-bool frontendOnly = false;
+bool generateXML = false;
 bool generateDot = false;
 
-typedef Vector<const char *> ArgsVector;
-ArgsVector frontendArgs;
-ArgsVector backendArgs;
+/* Target language and output style. */
+CodeStyle codeStyle = GenTables;
+
+int numSplitPartitions = 0;
+bool noLineDirectives = false;
+
+bool displayPrintables = false;
+
+/* Target ruby impl */
+RubyImplEnum rubyImpl = MRI;
 
 /* Print a summary of the options. */
 void usage()
@@ -86,41 +101,83 @@ void usage()
 "   -v, --version        Print version information and exit\n"
 "   -o <file>            Write output to <file>\n"
 "   -s                   Print some statistics on stderr\n"
+"   -d                   Do not remove duplicates from action lists\n"
+"   -I <dir>             Add <dir> to the list of directories to search\n"
+"                        for included an imported files\n"
+"error reporting format:\n"
+"   --error-format=gnu   file:line:column: message (default)\n"
+"   --error-format=msvc  file(line,column): message\n"
 "fsm minimization:\n"
 "   -n                   Do not perform minimization\n"
 "   -m                   Minimize at the end of the compilation\n"
 "   -l                   Minimize after most operations (default)\n"
 "   -e                   Minimize after every operation\n"
 "visualization:\n"
+"   -x                   Run the frontend only: emit XML intermediate format\n"
 "   -V                   Generate a dot file for Graphviz\n"
 "   -p                   Display printable characters on labels\n"
-"   -S <spec>            FSM specification to output (for rlgen-dot)\n"
-"   -M <machine>         Machine definition/instantiation to output (for rlgen-dot)\n"
+"   -S <spec>            FSM specification to output (for graphviz output)\n"
+"   -M <machine>         Machine definition/instantiation to output (for graphviz output)\n"
 "host language:\n"
 "   -C                   The host language is C, C++, Obj-C or Obj-C++ (default)\n"
 "   -D                   The host language is D\n"
 "   -J                   The host language is Java\n"
 "   -R                   The host language is Ruby\n"
-"line direcives: (C/D only)\n"
+"   -A                   The host language is C#\n"
+"line direcives: (C/D/C#)\n"
 "   -L                   Inhibit writing of #line directives\n"
-"code style: (C/Ruby only)\n"
+"code style: (C/D/Java/Ruby/C#)\n"
 "   -T0                  Table driven FSM (default)\n"
+"code style: (C/D/Ruby/C#)\n"
 "   -T1                  Faster table driven FSM\n"
 "   -F0                  Flat table driven FSM\n"
 "   -F1                  Faster flat table-driven FSM\n"
-"code style: (C only)\n"
+"code style: (C/D/C#)\n"
 "   -G0                  Goto-driven FSM\n"
 "   -G1                  Faster goto-driven FSM\n"
+"code style: (C/D)\n"
 "   -G2                  Really fast goto-driven FSM\n"
 "   -P<N>                N-Way Split really fast goto-driven FSM\n"
        ;       
+
+       exit(0);
 }
 
-/* Print version information. */
+/* Print version information and exit. */
 void version()
 {
        cout << "Ragel State Machine Compiler version " VERSION << " " PUBDATE << endl <<
-                       "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
+                       "Copyright (c) 2001-2009 by Adrian Thurston" << endl;
+       exit(0);
+}
+
+/* Error reporting format. */
+ErrorFormat errorFormat = ErrorFormatGNU;
+
+InputLoc makeInputLoc( const char *fileName, int line, int col)
+{
+       InputLoc loc = { fileName, line, col };
+       return loc;
+}
+
+ostream &operator<<( ostream &out, const InputLoc &loc )
+{
+       assert( loc.fileName != 0 );
+       switch ( errorFormat ) {
+       case ErrorFormatMSVC:
+               out << loc.fileName << "(" << loc.line;
+               if ( loc.col )
+                       out << "," << loc.col;
+               out << ")";
+               break;
+
+       default:
+               out << loc.fileName << ":" << loc.line;
+               if ( loc.col )
+                       out << ":" << loc.col;
+               break;
+       }
+       return out;
 }
 
 /* Total error count. */
@@ -129,9 +186,7 @@ int gblErrorCount = 0;
 /* Print the opening to a warning in the input, then return the error ostream. */
 ostream &warning( const InputLoc &loc )
 {
-       assert( loc.fileName != 0 );
-       cerr << loc.fileName << ":" << loc.line << ":" << 
-                       loc.col << ": warning: ";
+       cerr << loc << ": warning: ";
        return cerr;
 }
 
@@ -146,8 +201,7 @@ ostream &error()
 ostream &error( const InputLoc &loc )
 {
        gblErrorCount += 1;
-       assert( loc.fileName != 0 );
-       cerr << loc.fileName << ":" << loc.line << ": ";
+       cerr << loc << ": ";
        return cerr;
 }
 
@@ -161,9 +215,11 @@ void escapeLineDirectivePath( std::ostream &out, char *path )
        }
 }
 
-void processArgs( int argc, char **argv, char *&inputFileName, char *&outputFileName )
+void processArgs( int argc, const char **argv, InputData &id )
 {
-       ParamCheck pc("fo:nmleabjkS:M:CDJRvHh?-:sT:F:G:P:LpV", argc, argv);
+       ParamCheck pc("xo:dnmleabjkS:M:I:CDJRAvHh?-:sT:F:G:P:LpV", argc, argv);
+
+       /* FIXME: Need to check code styles VS langauge. */
 
        while ( pc.check() ) {
                switch ( pc.state ) {
@@ -173,149 +229,190 @@ void processArgs( int argc, char **argv, char *&inputFileName, char *&outputFile
                                generateDot = true;
                                break;
 
-                       case 'f':
-                               frontendOnly = true;
+                       case 'x':
+                               generateXML = true;
                                break;
 
                        /* Output. */
                        case 'o':
-                               if ( *pc.parameterArg == 0 )
+                               if ( *pc.paramArg == 0 )
                                        error() << "a zero length output file name was given" << endl;
-                               else if ( outputFileName != 0 )
+                               else if ( id.outputFileName != 0 )
                                        error() << "more than one output file name was given" << endl;
                                else {
                                        /* Ok, remember the output file name. */
-                                       outputFileName = pc.parameterArg;
+                                       id.outputFileName = pc.paramArg;
                                }
                                break;
 
+                       /* Flag for turning off duplicate action removal. */
+                       case 'd':
+                               wantDupsRemoved = false;
+                               break;
+
                        /* Minimization, mostly hidden options. */
                        case 'n':
                                minimizeOpt = MinimizeNone;
-                               frontendArgs.append( "-n" );
                                break;
                        case 'm':
                                minimizeOpt = MinimizeEnd;
-                               frontendArgs.append( "-m" );
                                break;
                        case 'l':
                                minimizeOpt = MinimizeMostOps;
-                               frontendArgs.append( "-l" );
                                break;
                        case 'e':
                                minimizeOpt = MinimizeEveryOp;
-                               frontendArgs.append( "-e" );
                                break;
                        case 'a':
                                minimizeLevel = MinimizeApprox;
-                               frontendArgs.append( "-a" );
                                break;
                        case 'b':
                                minimizeLevel = MinimizeStable;
-                               frontendArgs.append( "-b" );
                                break;
                        case 'j':
                                minimizeLevel = MinimizePartition1;
-                               frontendArgs.append( "-j" );
                                break;
                        case 'k':
                                minimizeLevel = MinimizePartition2;
-                               frontendArgs.append( "-k" );
                                break;
 
                        /* Machine spec. */
                        case 'S':
-                               if ( *pc.parameterArg == 0 )
+                               if ( *pc.paramArg == 0 )
                                        error() << "please specify an argument to -S" << endl;
                                else if ( machineSpec != 0 )
                                        error() << "more than one -S argument was given" << endl;
                                else {
                                        /* Ok, remember the path to the machine to generate. */
-                                       machineSpec = pc.parameterArg;
-                                       frontendArgs.append( "-S" );
-                                       frontendArgs.append( pc.parameterArg );
+                                       machineSpec = pc.paramArg;
                                }
                                break;
 
                        /* Machine path. */
                        case 'M':
-                               if ( *pc.parameterArg == 0 )
+                               if ( *pc.paramArg == 0 )
                                        error() << "please specify an argument to -M" << endl;
                                else if ( machineName != 0 )
                                        error() << "more than one -M argument was given" << endl;
                                else {
                                        /* Ok, remember the machine name to generate. */
-                                       machineName = pc.parameterArg;
-                                       frontendArgs.append( "-M" );
-                                       frontendArgs.append( pc.parameterArg );
+                                       machineName = pc.paramArg;
+                               }
+                               break;
+
+                       case 'I':
+                               if ( *pc.paramArg == 0 )
+                                       error() << "please specify an argument to -I" << endl;
+                               else {
+                                       id.includePaths.append( pc.paramArg );
                                }
                                break;
 
                        /* Host language types. */
                        case 'C':
                                hostLang = &hostLangC;
-                               frontendArgs.append( "-C" );
                                break;
                        case 'D':
                                hostLang = &hostLangD;
-                               frontendArgs.append( "-D" );
                                break;
                        case 'J':
                                hostLang = &hostLangJava;
-                               frontendArgs.append( "-J" );
                                break;
                        case 'R':
                                hostLang = &hostLangRuby;
-                               frontendArgs.append( "-R" );
+                               break;
+                       case 'A':
+                               hostLang = &hostLangCSharp;
                                break;
 
                        /* Version and help. */
                        case 'v':
                                version();
-                               exit(0);
+                               break;
                        case 'H': case 'h': case '?':
                                usage();
-                               exit(0);
+                               break;
                        case 's':
                                printStatistics = true;
-                               frontendArgs.append( "-s" );
                                break;
-                       case '-':
-                               if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
+                       case '-': {
+                               char *arg = strdup( pc.paramArg );
+                               char *eq = strchr( arg, '=' );
+
+                               if ( eq != 0 )
+                                       *eq++ = 0;
+
+                               if ( strcmp( arg, "help" ) == 0 )
                                        usage();
-                                       exit(0);
-                               }
-                               else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
+                               else if ( strcmp( arg, "version" ) == 0 )
                                        version();
-                                       exit(0);
+                               else if ( strcmp( arg, "error-format" ) == 0 ) {
+                                       if ( eq == 0 )
+                                               error() << "expecting '=value' for error-format" << endl;
+                                       else if ( strcmp( eq, "gnu" ) == 0 )
+                                               errorFormat = ErrorFormatGNU;
+                                       else if ( strcmp( eq, "msvc" ) == 0 )
+                                               errorFormat = ErrorFormatMSVC;
+                                       else
+                                               error() << "invalid value for error-format" << endl;
                                }
+                               else if ( strcmp( arg, "rbx" ) == 0 )
+                                       rubyImpl = Rubinius;
                                else {
-                                       error() << "--" << pc.parameterArg << 
+                                       error() << "--" << pc.paramArg << 
                                                        " is an invalid argument" << endl;
                                }
+                               free( arg );
+                               break;
+                       }
 
                        /* Passthrough args. */
                        case 'T': 
-                               backendArgs.append( "-T" );
-                               backendArgs.append( pc.parameterArg );
+                               if ( pc.paramArg[0] == '0' )
+                                       codeStyle = GenTables;
+                               else if ( pc.paramArg[0] == '1' )
+                                       codeStyle = GenFTables;
+                               else {
+                                       error() << "-T" << pc.paramArg[0] << 
+                                                       " is an invalid argument" << endl;
+                                       exit(1);
+                               }
                                break;
                        case 'F': 
-                               backendArgs.append( "-F" );
-                               backendArgs.append( pc.parameterArg );
+                               if ( pc.paramArg[0] == '0' )
+                                       codeStyle = GenFlat;
+                               else if ( pc.paramArg[0] == '1' )
+                                       codeStyle = GenFFlat;
+                               else {
+                                       error() << "-F" << pc.paramArg[0] << 
+                                                       " is an invalid argument" << endl;
+                                       exit(1);
+                               }
                                break;
                        case 'G': 
-                               backendArgs.append( "-G" );
-                               backendArgs.append( pc.parameterArg );
+                               if ( pc.paramArg[0] == '0' )
+                                       codeStyle = GenGoto;
+                               else if ( pc.paramArg[0] == '1' )
+                                       codeStyle = GenFGoto;
+                               else if ( pc.paramArg[0] == '2' )
+                                       codeStyle = GenIpGoto;
+                               else {
+                                       error() << "-G" << pc.paramArg[0] << 
+                                                       " is an invalid argument" << endl;
+                                       exit(1);
+                               }
                                break;
                        case 'P':
-                               backendArgs.append( "-P" );
-                               backendArgs.append( pc.parameterArg );
+                               codeStyle = GenSplit;
+                               numSplitPartitions = atoi( pc.paramArg );
                                break;
+
                        case 'p':
-                               backendArgs.append( "-p" );
+                               displayPrintables = true;
                                break;
+
                        case 'L':
-                               backendArgs.append( "-L" );
+                               noLineDirectives = true;
                                break;
                        }
                        break;
@@ -328,347 +425,119 @@ void processArgs( int argc, char **argv, char *&inputFileName, char *&outputFile
                        /* It is interpreted as an input file. */
                        if ( *pc.curArg == 0 )
                                error() << "a zero length input file name was given" << endl;
-                       else if ( inputFileName != 0 )
+                       else if ( id.inputFileName != 0 )
                                error() << "more than one input file name was given" << endl;
                        else {
                                /* OK, Remember the filename. */
-                               inputFileName = pc.curArg;
+                               id.inputFileName = pc.curArg;
                        }
                        break;
                }
        }
 }
 
-int frontend( char *inputFileName, char *outputFileName )
+void process( InputData &id )
 {
        /* Open the input file for reading. */
-       assert( inputFileName != 0 );
-       ifstream *inFile = new ifstream( inputFileName );
-       istream *inStream = inFile;
+       assert( id.inputFileName != 0 );
+       ifstream *inFile = new ifstream( id.inputFileName );
        if ( ! inFile->is_open() )
-               error() << "could not open " << inputFileName << " for reading" << endp;
+               error() << "could not open " << id.inputFileName << " for reading" << endp;
 
        /* Used for just a few things. */
        std::ostringstream hostData;
 
-       if ( machineSpec == 0 && machineName == 0 )
-               hostData << "<host line=\"1\" col=\"1\">";
+       /* Make the first input item. */
+       InputItem *firstInputItem = new InputItem;
+       firstInputItem->type = InputItem::HostData;
+       firstInputItem->loc.fileName = id.inputFileName;
+       firstInputItem->loc.line = 1;
+       firstInputItem->loc.col = 1;
+       id.inputItems.append( firstInputItem );
 
-       Scanner scanner( inputFileName, *inStream, hostData, 0, 0, 0, false );
+       Scanner scanner( id, id.inputFileName, *inFile, 0, 0, 0, false );
        scanner.do_scan();
 
        /* Finished, final check for errors.. */
        if ( gblErrorCount > 0 )
-               return 1;
-       
+               exit(1);
+
        /* Now send EOF to all parsers. */
-       terminateAllParsers();
+       id.terminateAllParsers();
 
-       /* Finished, final check for errors.. */
+       /* Bail on above error. */
        if ( gblErrorCount > 0 )
-               return 1;
+               exit(1);
 
-       if ( machineSpec == 0 && machineName == 0 )
-               hostData << "</host>\n";
+       /* Locate the backend program */
+       /* Compiles machines. */
+       id.prepareMachineGen();
 
        if ( gblErrorCount > 0 )
-               return 1;
-       
-       ostream *outputFile = 0;
-       if ( outputFileName != 0 )
-               outputFile = new ofstream( outputFileName );
-       else
-               outputFile = &cout;
-
-       /* Write the machines, then the surrounding code. */
-       writeMachines( *outputFile, hostData.str(), inputFileName );
-
-       /* Close the intermediate file. */
-       if ( outputFileName != 0 )
-               delete outputFile;
-
-       return gblErrorCount > 0;
-}
-
-char *makeIntermedTemplate( char *baseFileName )
-{
-       char *result = 0;
-       const char *templ = "ragel-XXXXXX.xml";
-       char *lastSlash = strrchr( baseFileName, '/' );
-       if ( lastSlash == 0 ) {
-               result = new char[strlen(templ)+1];
-               strcpy( result, templ );
-       }
-       else {
-               int baseLen = lastSlash - baseFileName + 1;
-               result = new char[baseLen + strlen(templ) + 1];
-               memcpy( result, baseFileName, baseLen );
-               strcpy( result+baseLen, templ );
-       }
-       return result;
-};
-
-char *openIntermed( char *inputFileName, char *outputFileName )
-{
-       srand(time(0));
-       char *result = 0;
-
-       /* Which filename do we use as the base? */
-       char *baseFileName = outputFileName != 0 ? outputFileName : inputFileName;
-
-       /* The template for the intermediate file name. */
-       char *intermedFileName = makeIntermedTemplate( baseFileName );
-
-       /* Randomize the name and try to open. */
-       char fnChars[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-       char *firstX = strrchr( intermedFileName, 'X' ) - 5;
-       for ( int tries = 0; tries < 20; tries++ ) {
-               /* Choose a random name. */
-               for ( int x = 0; x < 6; x++ )
-                       firstX[x] = fnChars[rand() % 52];
-
-               /* Try to open the file. */
-               int fd = ::open( intermedFileName, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR );
-
-               if ( fd > 0 ) {
-                       /* Success. Close the file immediately and return the name for use
-                        * by the child processes. */
-                       ::close( fd );
-                       result = intermedFileName;
-                       break;
-               }
-
-               if ( errno == EACCES ) {
-                       error() << "failed to open temp file " << intermedFileName << 
-                                       ", access denied" << endp;
-               }
-       }
-
-       if ( result == 0 )
-               error() << "abnormal error: cannot find unique name for temp file" << endp;
+               exit(1);
 
-       return result;
-}
+       id.makeOutputStream();
 
+       /* Generates the reduced machine, which we use to write output. */
+       if ( !generateXML ) {
+               id.generateReduced();
 
-void cleanExit( char *intermed, int status )
-{
-       unlink( intermed );
-       exit( status );
-}
-
-#ifndef WIN32
-
-/* If any forward slash is found in argv0 then it is assumed that the path is
- * explicit and the path to the backend executable should be derived from
- * that. Whe check that location and also go up one then inside a directory of
- * the same name in case we are executing from the source tree. If no forward
- * slash is found it is assumed the file is being run from the installed
- * location. The PREFIX supplied during configuration is used. */
-char **makePathChecksUnix( const char *argv0, const char *progName )
-{
-       char **result = new char*[3];
-       const char *lastSlash = strrchr( argv0, '/' );
-       int numChecks = 0;
-
-       if ( lastSlash != 0 ) {
-               char *path = strdup( argv0 );
-               int givenPathLen = (lastSlash - argv0) + 1;
-               path[givenPathLen] = 0;
-
-               int progNameLen = strlen(progName);
-               int length = givenPathLen + progNameLen + 1;
-               char *check = new char[length];
-               sprintf( check, "%s%s", path, progName );
-               result[numChecks++] = check;
-
-               length = givenPathLen + 3 + progNameLen + 1 + progNameLen + 1;
-               check = new char[length];
-               sprintf( check, "%s../%s/%s", path, progName, progName );
-               result[numChecks++] = check;
+               if ( gblErrorCount > 0 )
+                       exit(1);
        }
-       else {
-               int prefixLen = strlen(PREFIX);
-               int progNameLen = strlen(progName);
-               int length = prefixLen + 5 + progNameLen + 1;
-               char *check = new char[length];
 
-               sprintf( check, PREFIX "/bin/%s", progName );
-               result[numChecks++] = check;
-       }
+       id.verifyWritesHaveData();
+       if ( gblErrorCount > 0 )
+               exit(1);
 
-       result[numChecks] = 0;
-       return result;
-}
+       /*
+        * From this point on we should not be reporting any errors.
+        */
 
+       id.openOutput();
+       id.writeOutput();
 
-void forkAndExec( const char *progName, char **pathChecks, 
-               ArgsVector &args, char *intermed )
-{
-       pid_t pid = fork();
-       if ( pid < 0 ) {
-               /* Error, no child created. */
-               error() << "failed to fork for " << progName << endl;
-               cleanExit( intermed, 1 );
-       }
-       else if ( pid == 0 ) {
-               /* child */
-               while ( *pathChecks != 0 ) {
-                       /* Execv does not modify argv, it just uses the const form that is
-                        * compatible with the most code. Ours not included. */
-                       execv( *pathChecks, (char *const*) args.data );
-                       pathChecks += 1;
-               }
-               error() << "failed to exec " << progName << endl;
-               cleanExit( intermed, 1 );
-       }
+       /* Close the input and the intermediate file. */
+       delete inFile;
 
-       /* Parent process, wait for the child. */
-       int status;
-       wait( &status );
-
-       /* What happened with the child. */
-       if ( ! WIFEXITED( status ) ) {
-               error() << progName << " did not exit normally" << endl;
-               cleanExit( intermed, 1 );
+       /* If writing to a file, delete the ostream, causing it to flush.
+        * Standard out is flushed automatically. */
+       if ( id.outputFileName != 0 ) {
+               delete id.outStream;
+               delete id.outFilter;
        }
-       
-       if ( WEXITSTATUS(status) != 0 )
-               cleanExit( intermed, WEXITSTATUS(status) );
-}
 
-#else
-
-/* GetModuleFileNameEx is used to find out where the the current process's
- * binary is. That location is searched first. If that fails then we go up one
- * directory and look for the executable inside a directory of the same name
- * in case we are executing from the source tree.
- * */
-char **makePathChecksWin( const char *progName )
-{
-       int len = 1024;
-       char *imageFileName = new char[len];
-       HANDLE h = GetCurrentProcess();
-       len = GetModuleFileNameEx( h, NULL, imageFileName, len );
-       imageFileName[len] = 0;
-
-       char **result = new char*[3];
-       const char *lastSlash = strrchr( imageFileName, '\\' );
-       int numChecks = 0;
-
-       assert( lastSlash != 0 );
-       char *path = strdup( imageFileName );
-       int givenPathLen = (lastSlash - imageFileName) + 1;
-       path[givenPathLen] = 0;
-
-       int progNameLen = strlen(progName);
-       int length = givenPathLen + progNameLen + 1;
-       char *check = new char[length];
-       sprintf( check, "%s%s", path, progName );
-       result[numChecks++] = check;
-
-       length = givenPathLen + 3 + progNameLen + 1 + progNameLen + 1;
-       check = new char[length];
-       sprintf( check, "%s..\\%s\\%s", path, progName, progName );
-       result[numChecks++] = check;
-
-       result[numChecks] = 0;
-       return result;
+       assert( gblErrorCount == 0 );
 }
 
-void spawn( char *progName, char **pathChecks, 
-               ArgsVector &args, char *intermed )
+char *makeIntermedTemplate( const char *baseFileName )
 {
-       int result = 0;
-       while ( *pathChecks != 0 ) {
-               cerr << "trying to execute " << *pathChecks << endl;
-               result = _spawnv( _P_WAIT, *pathChecks, args.data );
-               if ( result >= 0 || errno != ENOENT )
-                       break;
-               pathChecks += 1;
-       }
-
-       if ( result < 0 ) {
-               error() << "failed to spawn " << progName << endl;
-               cleanExit( intermed, 1 );
+       char *result = 0;
+       const char *templ = "ragel-XXXXXX.xml";
+       const char *lastSlash = strrchr( baseFileName, '/' );
+       if ( lastSlash == 0 ) {
+               result = new char[strlen(templ)+1];
+               strcpy( result, templ );
        }
-
-       if ( result > 0 )
-               cleanExit( intermed, 1 );
-}
-
-#endif
-
-void execFrontend( const char *argv0, char *inputFileName, char *intermed )
-{
-       /* The frontend program name. */
-       const char *progName = "ragel";
-
-       frontendArgs.insert( 0, progName );
-       frontendArgs.insert( 1, "-f" );
-       frontendArgs.append( "-o" );
-       frontendArgs.append( intermed );
-       frontendArgs.append( inputFileName );
-       frontendArgs.append( 0 );
-
-#ifndef WIN32
-       char **pathChecks = makePathChecksUnix( argv0, progName );
-       forkAndExec( progName, pathChecks, frontendArgs, intermed );
-#else
-       char **pathChecks = makePathChecksWin( progName );
-       spawn( progName, pathChecks, frontendArgs, intermed );
-#endif
-}
-
-void execBackend( const char *argv0, char *intermed, char *outputFileName )
-{
-       /* Locate the backend program */
-       const char *progName = 0;
-       if ( generateDot )
-               progName = "rlgen-dot";
        else {
-               switch ( hostLang->lang ) {
-                       case HostLang::C:
-                       case HostLang::D:
-                               progName = "rlgen-cd";
-                               break;
-                       case HostLang::Java:
-                               progName = "rlgen-java";
-                               break;
-                       case HostLang::Ruby:
-                               progName = "rlgen-ruby";
-                               break;
-               }
-       }
-
-       backendArgs.insert( 0, progName );
-       if ( outputFileName != 0 ) {
-               backendArgs.append( "-o" );
-               backendArgs.append( outputFileName );
+               int baseLen = lastSlash - baseFileName + 1;
+               result = new char[baseLen + strlen(templ) + 1];
+               memcpy( result, baseFileName, baseLen );
+               strcpy( result+baseLen, templ );
        }
-       backendArgs.append( intermed );
-       backendArgs.append( 0 );
-
-#ifndef WIN32
-       char **pathChecks = makePathChecksUnix( argv0, progName );
-       forkAndExec( progName, pathChecks, backendArgs, intermed );
-#else
-       char **pathChecks = makePathChecksWin( progName );
-       spawn( progName, pathChecks, backendArgs, intermed );
-#endif
-}
+       return result;
+};
 
 /* Main, process args and call yyparse to start scanning input. */
-int main(int argc, char **argv)
+int main( int argc, const char **argv )
 {
-       char *inputFileName = 0;
-       char *outputFileName = 0;
+       InputData id;
 
-       processArgs( argc, argv, inputFileName, outputFileName );
+       processArgs( argc, argv, id );
 
        /* Require an input file. If we use standard in then we won't have a file
         * name on which to base the output. */
-       if ( inputFileName == 0 )
+       if ( id.inputFileName == 0 )
                error() << "no input file given" << endl;
 
        /* Bail on argument processing errors. */
@@ -676,26 +545,14 @@ int main(int argc, char **argv)
                exit(1);
 
        /* Make sure we are not writing to the same file as the input file. */
-       if ( inputFileName != 0 && outputFileName != 0 && 
-                       strcmp( inputFileName, outputFileName  ) == 0 )
+       if ( id.inputFileName != 0 && id.outputFileName != 0 && 
+                       strcmp( id.inputFileName, id.outputFileName  ) == 0 )
        {
-               error() << "output file \"" << outputFileName  << 
+               error() << "output file \"" << id.outputFileName  << 
                                "\" is the same as the input file" << endp;
        }
 
-       if ( frontendOnly )
-               return frontend( inputFileName, outputFileName );
-
-       char *intermed = openIntermed( inputFileName, outputFileName );
-
-       /* From here on in the cleanExit function should be used to exit. */
-
-       /* Run the frontend, then the backend processes. */
-       execFrontend( argv[0], inputFileName, intermed );
-       execBackend( argv[0], intermed, outputFileName );
-
-       /* Clean up the intermediate. */
-       cleanExit( intermed, 0 );
+       process( id );
 
        return 0;
 }