You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sip4-tqt/sipgen/main.c

516 lines
12 KiB

/*
* The main module for SIP.
*
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "sip.h"
#ifndef PACKAGE
#define PACKAGE "sip"
#endif
#define VERSION "4.10.5"
/* Global variables - see sip.h for their meaning. */
char *sipVersion;
stringList *includeDirList;
static char *sipPackage = PACKAGE;
static int warnings = FALSE;
static void help(void);
static void version(void);
static void usage(void);
static char parseopt(int,char **,char *,char **,int *,char **);
static int parseInt(char *,char);
int main(int argc, char **argv)
{
char *filename, *docFile, *codeDir, *srcSuffix, *flagFile, *consModule;
char arg, *optarg, *buildFile, *apiFile, *xmlFile;
int optnr, exceptions, tracing, releaseGIL, parts, kwdArgs, protHack, docs;
FILE *file;
sipSpec spec;
stringList *versions, *xfeatures;
/* Initialise. */
sipVersion = VERSION;
includeDirList = NULL;
versions = NULL;
xfeatures = NULL;
buildFile = NULL;
codeDir = NULL;
docFile = NULL;
srcSuffix = NULL;
flagFile = NULL;
apiFile = NULL;
xmlFile = NULL;
consModule = NULL;
exceptions = FALSE;
tracing = FALSE;
releaseGIL = FALSE;
parts = 0;
kwdArgs = FALSE;
protHack = FALSE;
docs = FALSE;
/* Parse the command line. */
optnr = 1;
while ((arg = parseopt(argc, argv, "hVa:b:ec:d:gI:j:km:op:Prs:t:wx:z:", &flagFile, &optnr, &optarg)) != '\0')
switch (arg)
{
case 'o':
/* Generate docstrings. */
docs = TRUE;
break;
case 'p':
/* The name of the consolidated module. */
consModule = optarg;
break;
case 'P':
/* Enable the protected/public hack. */
protHack = TRUE;
break;
case 'a':
/* Where to generate the API file. */
apiFile = optarg;
break;
case 'm':
/* Where to generate the XML file. */
xmlFile = optarg;
break;
case 'b':
/* Generate a build file. */
buildFile = optarg;
break;
case 'e':
/* Enable exceptions. */
exceptions = TRUE;
break;
case 'g':
/* Always release the GIL. */
releaseGIL = TRUE;
break;
case 'j':
/* Generate the code in this number of parts. */
parts = parseInt(optarg,'j');
break;
case 'z':
/* Read a file for the next flags. */
if (flagFile != NULL)
fatal("The -z flag cannot be specified in an argument file\n");
flagFile = optarg;
break;
case 'c':
/* Where to generate the code. */
codeDir = optarg;
break;
case 'd':
/* Where to generate the documentation. */
docFile = optarg;
break;
case 't':
/* Which platform or version to generate code for. */
appendString(&versions,optarg);
break;
case 'x':
/* Which features are disabled. */
appendString(&xfeatures,optarg);
break;
case 'I':
/* Where to get included files from. */
appendString(&includeDirList,optarg);
break;
case 'r':
/* Enable tracing. */
tracing = TRUE;
break;
case 's':
/* The suffix to use for source files. */
srcSuffix = optarg;
break;
case 'w':
/* Enable warning messages. */
warnings = TRUE;
break;
case 'k':
/* Allow keyword arguments in functions and methods. */
kwdArgs = TRUE;
break;
case 'h':
/* Help message. */
help();
break;
case 'V':
/* Display the version number. */
version();
break;
default:
usage();
}
if (optnr < argc)
{
file = NULL;
filename = argv[optnr++];
if (optnr < argc)
usage();
}
else
{
file = stdin;
filename = "stdin";
}
/* Parse the input file. */
parse(&spec, file, filename, versions, xfeatures, kwdArgs, protHack);
/* Verify and transform the parse tree. */
transform(&spec);
/* Generate code. */
generateCode(&spec, codeDir, buildFile, docFile, srcSuffix, exceptions,
tracing, releaseGIL, parts, xfeatures, consModule, docs);
/* Generate the API file. */
if (apiFile != NULL)
generateAPI(&spec, spec.module, apiFile);
/* Generate the XML export. */
if (xmlFile != NULL)
generateXML(&spec, spec.module, xmlFile);
/* All done. */
return 0;
}
/*
* Parse the next command line argument - similar to UNIX getopts(). Allow a
* flag to specify that a file contains further arguments.
*/
static char parseopt(int argc, char **argv, char *opts, char **flags,
int *optnrp, char **optargp)
{
char arg, *op, *fname;
int optnr;
static FILE *fp = NULL;
/* Deal with any file first. */
fname = *flags;
if (fname != NULL && fp == NULL && (fp = fopen(fname,"r")) == NULL)
fatal("Unable to open %s\n",fname);
if (fp != NULL)
{
char buf[200], *cp, *fname;
int ch;
fname = *flags;
cp = buf;
while ((ch = fgetc(fp)) != EOF)
{
/* Skip leading whitespace. */
if (cp == buf && isspace(ch))
continue;
if (ch == '\n')
break;
if (cp == &buf[sizeof (buf) - 1])
fatal("A flag in %s is too long\n",fname);
*cp++ = (char)ch;
}
*cp = '\0';
if (ch == EOF)
{
fclose(fp);
fp = NULL;
*flags = NULL;
}
/*
* Get the option character and any optional argument from the
* line.
*/
if (buf[0] != '\0')
{
if (buf[0] != '-' || buf[1] == '\0')
fatal("An non-flag was given in %s\n",fname);
arg = buf[1];
/* Find any optional argument. */
for (cp = &buf[2]; *cp != '\0'; ++cp)
if (!isspace(*cp))
break;
if (*cp == '\0')
cp = NULL;
else
cp = sipStrdup(cp);
*optargp = cp;
if ((op = strchr(opts,arg)) == NULL)
fatal("An invalid flag was given in %s\n",fname);
if (op[1] == ':' && cp == NULL)
fatal("Missing flag argument in %s\n",fname);
if (op[1] != ':' && cp != NULL)
fatal("Unexpected flag argument in %s\n",fname);
return arg;
}
}
/* Check there is an argument and it is a switch. */
optnr = *optnrp;
if (optnr >= argc || argv[optnr] == NULL || argv[optnr][0] != '-')
return '\0';
/* Check it is a valid switch. */
arg = argv[optnr][1];
if (arg == '\0' || (op = strchr(opts,arg)) == NULL)
usage();
/* Check for the switch parameter, if any. */
if (op[1] == ':')
{
if (argv[optnr][2] != '\0')
{
*optargp = &argv[optnr][2];
++optnr;
}
else if (optnr + 1 >= argc || argv[optnr + 1] == NULL)
usage();
else
{
*optargp = argv[optnr + 1];
optnr += 2;
}
}
else if (argv[optnr][2] != '\0')
usage();
else
{
*optargp = NULL;
++optnr;
}
*optnrp = optnr;
return arg;
}
/*
* Parse an integer option.
*/
static int parseInt(char *arg, char opt)
{
char *endptr;
int val;
val = strtol(arg, &endptr, 10);
if (*arg == '\0' || *endptr != '\0')
fatal("Invalid integer argument for -%c flag\n", opt);
return val;
}
/*
* Append a string to a list of them.
*/
void appendString(stringList **headp, const char *s)
{
stringList *sl;
/* Create the new entry. */
sl = sipMalloc(sizeof (stringList));
sl -> s = s;
sl -> next = NULL;
/* Append it to the list. */
while (*headp != NULL)
headp = &(*headp) -> next;
*headp = sl;
}
/*
* Display a warning message.
*/
void warning(char *fmt,...)
{
static int start = TRUE;
va_list ap;
if (!warnings)
return;
if (start)
{
fprintf(stderr,"%s: Warning: ",sipPackage);
start = FALSE;
}
va_start(ap,fmt);
vfprintf(stderr,fmt,ap);
va_end(ap);
if (strchr(fmt,'\n') != NULL)
start = TRUE;
}
/*
* Display all or part of a one line error message describing a fatal error.
* If the message is complete (it has a newline) then the program exits.
*/
void fatal(char *fmt,...)
{
static int start = TRUE;
va_list ap;
if (start)
{
fprintf(stderr,"%s: ",sipPackage);
start = FALSE;
}
va_start(ap,fmt);
vfprintf(stderr,fmt,ap);
va_end(ap);
if (strchr(fmt,'\n') != NULL)
exit(1);
}
/*
* Display the SIP version number on stdout and exit with zero exit status.
*/
static void version(void)
{
printf("%s\n",sipVersion);
exit(0);
}
/*
* Display the help message on stdout and exit with zero exit status.
*/
static void help(void)
{
printf(
"Usage:\n"
" %s [-h] [-V] [-a file] [-b file] [-c dir] [-d file] [-e] [-g] [-I dir] [-j #] [-k] [-m file] [-o] [-p module] [-P] [-r] [-s suffix] [-t tag] [-w] [-x feature] [-z file] [file]\n"
"where:\n"
" -h display this help message\n"
" -V display the %s version number\n"
" -a file the name of the TQScintilla API file [default not generated]\n"
" -b file the name of the build file [default none generated]\n"
" -c dir the name of the code directory [default not generated]\n"
" -d file the name of the documentation file [default not generated]\n"
" -e enable support for exceptions [default disabled]\n"
" -g always release and reacquire the GIL [default only when specified]\n"
" -I dir look in this directory when including files\n"
" -j # split the generated code into # files [default 1 per class]\n"
" -k support keyword arguments in functions and methods\n"
" -m file the name of the XML export file [default not generated]\n"
" -o enable the automatic generation of docstrings [default disabled]\n"
" -p module the name of the consolidated module that this is a component of\n"
" -P enable the protected/public hack\n"
" -r generate code with tracing enabled [default disabled]\n"
" -s suffix the suffix to use for C or C++ source files [default \".c\" or \".cpp\"]\n"
" -t tag the version/platform to generate code for\n"
" -w enable warning messages\n"
" -x feature this feature is disabled\n"
" -z file the name of a file containing more command line flags\n"
" file the name of the specification file [default stdin]\n"
, sipPackage, sipPackage);
exit(0);
}
/*
* Display the usage message.
*/
static void usage(void)
{
fatal("Usage: %s [-h] [-V] [-a file] [-b file] [-c dir] [-d file] [-e] [-g] [-I dir] [-j #] [-k] [-m file] [-o] [-p module] [-P] [-r] [-s suffix] [-t tag] [-w] [-x feature] [-z file] [file]\n", sipPackage);
}