blob: 5fc2a8a05c2de5e3519e1280eab2d6d03b4ffc37 [file] [log] [blame]
/*
* main.c - Main program entry point for "treecc".
*
* Copyright (C) 2001, 2002 Southern Storm Software, Pty Ltd.
*
* 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 "parse.h"
#include "errors.h"
#include "gen.h"
#include "options.h"
#ifdef __cplusplus
extern "C" {
#endif
static void Usage(char *progname);
static void Version(void);
static int ExtraOptions(TreeCCContext *context, char **options, int num);
static char *GetDefault(const char *filename, const char *extension);
int main(int argc, char *argv[])
{
char *progname = argv[0];
char *opt;
char *outputFile = NULL;
char *headerFile = NULL;
char *extension = "c";
char *outputDir = NULL;
int forceCreate = 0;
TreeCCInput input;
TreeCCContext *context;
int sawStdin = 0;
int generateOutput = 1;
FILE *file;
int len, result;
char **options = (char **)malloc(sizeof(char *) * argc);
int num_options = 0;
/* Allocate the array for external "%option" values */
options = (char **)malloc(sizeof(char *) * argc);
if(!options)
{
TreeCCOutOfMemory(0);
}
/* Parse the command-line options */
while(argc > 1 && argv[1][0] == '-')
{
if(argv[1][1] == '\0')
{
/* The input is stdin */
break;
}
else if(argv[1][1] == '-')
{
/* The option begins with "--" */
if(argv[1][2] == '\0')
{
/* End of options, and start of filenames */
--argc;
++argv;
break;
}
if(!strcmp(argv[1], "--output"))
{
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
outputFile = argv[1];
}
else if(!strcmp(argv[1], "--header"))
{
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
headerFile = argv[1];
}
else if(!strcmp(argv[1], "--output-dir"))
{
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
outputDir = argv[1];
}
else if(!strcmp(argv[1], "--skeleton-dir"))
{
/* This option is obsolete: we still parse it just in
case there are older build systems that expect it */
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
}
else if(!strcmp(argv[1], "--extension"))
{
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
extension = argv[1];
}
else if(!strcmp(argv[1], "--option"))
{
--argc;
++argv;
if(argc <= 1)
{
Usage(progname);
return 1;
}
options[num_options++] = argv[1];
}
else if(!strcmp(argv[1], "--force-create"))
{
forceCreate = 1;
}
else if(!strcmp(argv[1], "--no-output"))
{
generateOutput = 0;
}
else if(!strcmp(argv[1], "--help"))
{
Usage(progname);
return 1;
}
else if(!strcmp(argv[1], "--version"))
{
Version();
return 0;
}
else
{
Usage(progname);
return 1;
}
}
else
{
/* Single-character option */
opt = argv[1] + 1;
while(*opt != '\0')
{
switch(*opt++)
{
case 'o':
{
if(*opt != '\0')
{
outputFile = opt;
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
outputFile = argv[1];
}
}
break;
case 'h':
{
if(*opt != '\0')
{
headerFile = opt;
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
headerFile = argv[1];
}
}
break;
case 'd':
{
if(*opt != '\0')
{
outputDir = opt;
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
outputDir = argv[1];
}
}
break;
case 's':
{
/* This option is obsolete: we still parse it just in
case there are older build systems that expect it */
if(*opt != '\0')
{
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
}
}
break;
case 'e':
{
if(*opt != '\0')
{
extension = opt;
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
extension = argv[1];
}
}
break;
case 'O':
{
if(*opt != '\0')
{
options[num_options++] = opt;
opt = "";
}
else if(argc <= 2)
{
Usage(progname);
return 1;
}
else
{
--argc;
++argv;
options[num_options++] = argv[1];
}
}
break;
case 'f':
{
forceCreate = 1;
}
break;
case 'n':
{
generateOutput = 0;
}
break;
case 'v':
{
Version();
return 0;
}
/* Not reached */
default:
{
Usage(progname);
return 1;
}
/* Not reached */
}
}
}
--argc;
++argv;
}
if(argc <= 1)
{
Usage(progname);
return 1;
}
/* Determine the default output and header filenames */
if(!outputFile)
{
if(*extension == '.')
{
++extension;
}
outputFile = GetDefault(argv[1], extension);
}
if(!headerFile)
{
headerFile = GetDefault(outputFile, "h");
}
/* Initialize the input routines */
TreeCCOpen(&input, progname, NULL, NULL);
/* Determine the default Java output directory */
if(!outputDir)
{
len = strlen(argv[1]);
while(len > 0 && argv[1][len - 1] != '/' && argv[1][len - 1] != '\\')
{
--len;
}
if(len > 0)
{
--len;
}
if(len > 0)
{
outputDir = (char *)malloc(len + 1);
if(!outputDir)
{
TreeCCOutOfMemory(&input);
}
strncpy(outputDir, argv[1], len);
outputDir[len] = '\0';
}
}
/* Create the parsing context */
context = TreeCCContextCreate(&input);
if(!context)
{
TreeCCOutOfMemory(&input);
}
context->force = forceCreate;
context->outputDirectory = outputDir;
/* Process additional options from the command-line */
if(!ExtraOptions(context, options, num_options))
{
return 1;
}
free(options);
/* Create the default source and header streams */
context->sourceStream = TreeCCStreamCreate(context, outputFile,
NULL, 0);
context->sourceStream->defaultFile = 1;
context->headerStream = TreeCCStreamCreate(context, headerFile,
NULL, 1);
context->headerStream->defaultFile = 1;
/* Process the input files */
while(argc > 1)
{
if(!strcmp(argv[1], "-"))
{
/* Parse stdin, but only once */
if(!sawStdin)
{
TreeCCOpen(&input, progname, stdin, "stdin");
TreeCCParse(context);
TreeCCClose(&input, 0);
sawStdin = 1;
}
}
else
{
/* Parse some other file */
if((file = fopen(argv[1], "r")) == NULL)
{
perror(argv[1]);
input.errors = 1;
}
else
{
TreeCCOpen(&input, progname, file, argv[1]);
TreeCCParse(context);
TreeCCClose(&input, 1);
}
}
--argc;
++argv;
}
/* Validate node and operation coverage */
TreeCCNodeValidate(context);
TreeCCOperationValidate(context);
/* Abort if we encountered errors during parsing and validation */
if(input.errors)
{
TreeCCContextDestroy(context);
return 1;
}
/* Generate the output files */
result = 0;
if(generateOutput)
{
TreeCCStream *stream;
TreeCCGenerate(context);
stream = context->streamList;
while(stream != 0)
{
if(!TreeCCStreamFlush(stream))
{
result = 1;
}
stream = stream->nextStream;
}
}
/* Done */
TreeCCContextDestroy(context);
return result;
}
static void Usage(char *progname)
{
fprintf(stderr, "TREECC " VERSION " - Tree Compiler-Compiler\n");
fprintf(stderr, "Copyright (c) 2001, 2002 Southern Storm Software, Pty Ltd.\n");
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [options] input ...\n", progname);
fprintf(stderr, "\n");
fprintf(stderr, " -o file, --output file\n");
fprintf(stderr, " Set the name of the output file.\n");
fprintf(stderr, " -h file, --header file\n");
fprintf(stderr, " Set the name of the header output file.\n");
fprintf(stderr, " -d dir, --output-dir file\n");
fprintf(stderr, " Set the name of the Java output directory.\n");
fprintf(stderr, " -e ext, --extension ext\n");
fprintf(stderr, " Set the output file extension (default is \".c\").\n");
fprintf(stderr, " -f, --force-create\n");
fprintf(stderr, " Force the creation of unchanged output files.\n");
fprintf(stderr, " -O opt, --option opt\n");
fprintf(stderr, " Set a treecc source option value.\n");
fprintf(stderr, " --help\n");
fprintf(stderr, " Print this help message.\n");
fprintf(stderr, " -v, --version\n");
fprintf(stderr, " Print the program version.\n");
}
static void Version(void)
{
printf("TREECC " VERSION " - Tree Compiler-Compiler\n");
printf("Copyright (c) 2001, 2002 Southern Storm Software, Pty Ltd.\n");
printf("\n");
printf("TREECC comes with ABSOLUTELY NO WARRANTY. This is free software,\n");
printf("and you are welcome to redistribute it under the terms of the\n");
printf("GNU General Public License. See the file COPYING for further details.\n");
printf("\n");
printf("Use the `--help' option to get help on the command-line options.\n");
}
/*
* Process extra treecc "%option" values from the command-line.
*/
static int ExtraOptions(TreeCCContext *context, char **options, int num)
{
char *name;
char *value;
while(num-- > 0)
{
name = *options++;
value = name;
while(*value != '\0' && *value != '=')
{
++value;
}
if(*value == '\0')
{
value = 0;
}
else
{
*value++ = '\0';
}
if(TreeCCOptionProcess(context, name, value) != TREECC_OPT_OK)
{
fprintf(stderr, "%s: unknown option or invalid value\n", name);
return 0;
}
}
return 1;
}
/*
* Get a default output or header filename from an input filename.
*/
static char *GetDefault(const char *filename, const char *extension)
{
int len;
char *name;
/* USe a default filename pattern if we are reading from stdin */
if(!strcmp(filename, "-"))
{
filename = "yy_tree.tc";
}
/* Find the end of the input filename, without its extension */
len = strlen(filename);
while(len > 0 && filename[len - 1] != '/' && filename[len - 1] != '\\' &&
filename[len - 1] != '.')
{
--len;
}
/* Construct the new filename */
if(len > 0 && filename[len - 1] == '.')
{
name = (char *)malloc(len + strlen(extension) + 1);
if(!name)
{
TreeCCOutOfMemory((TreeCCInput *)0);
}
strncpy(name, filename, len);
strcpy(name + len, extension);
}
else
{
len = strlen(filename);
name = (char *)malloc(len + strlen(extension) + 2);
if(!name)
{
TreeCCOutOfMemory((TreeCCInput *)0);
}
strcpy(name, filename);
name[len] = '.';
strcpy(name + len + 1, extension);
}
return name;
}
#ifdef __cplusplus
};
#endif