blob: fe908199e6b846a59d5b3d4047b7dacaab6207f0 [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008-2018 Texas Instruments Incorporated
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== xs.c ========
* This utility allows running XDC scripts outside of any object model.
*
* Usage: xs [-h] [-D<name>=<value>] [--xdcpath <xdcpath>]
* [--cp <classpath>] [--lp <java library path>] [-i] [--X<JVM opt>]
* [-m <module> | -[fc] <script> | <package> [args]]
*
* xs sets environment['xdc.root'] to the following values in order of
* preference: 1) -Dxdc.root option passed on the command line, 2) the
* directory containing this executable.
*
* The availiable options are partitioned into two groups: internal and
* external options.
*
* Internal options are subject to change and are intended for internal
* use only. Internal options always begin with '--'.
*
* External options are documented and must not be changed without a
* means for achieving backward compatibility with existing uses of xs.
* External options never begin with '--'.
*
* xs Options (<opts>):
* --xdcpath <xdcpath> add <xdcpath> to the package path
* --cp <cpath> adds <cpath> to automatically computed Java classpath
* --d turn on internal debug output. May be repeated;
* more --d's generates more optput.
* --X<opt> this option is passed directly to SUN JVM (minus
* one leading '-')
*
* --j <jar> add <jar> to classpath
*
* -g[=<opts>] run in "GUI debug" mode and pass optional <opts> to
* the debugger. If no options are passed, then the
* debugger stops at the first line of the specified
* script. Supported <opts> options:
* 'i' set initial breakpoint at start of the xs
* startup script, tconfini.tcf
*
* -i run in interactive mode
* -p <path> add <path> to list of directories to search for
* xs "system" files. This option allows platform
* developers to add a directory containing platform
* definitions not provided by TI.
*
* -D<name>=<val> add the "name=value" definition to the TCOM
* environment
*
* -v print the version and exit
*
* JavaScript Shell Options (in addition to -v passed to Shell when -i is
* passed to this executable):
* -[fc] <file> execute the script in the file <file>
* -m <module> execute the function 'main' in the module <module>
*
* Examples:
* Running a script with arguments:
* xs -f foo.xs hello world
*
* Running main() function in capsule in package pkg:
* xs -c pkg/cap.xs hello world
*
* Running main() function in module Mod in package pkg:
* xs -m pkg.Mod hello world
*
* Running main() function in module Main in package pkg:
* xs pkg
*/
#define GT_TRACE 1
#include <xdc/std.h>
#include <tcf.h>
#include <xdc/services/host/lib/gt.h>
#include <xdc/services/host/lib/xutl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#if defined(xdc_target__os_Windows)
#include <windows.h> /* GetModuleFileName */
#define DIRSTR "\\" /* directory separator character */
#define CALLBACK __stdcall /* Win32 callbacks use pascal conv */
#else
#define DIRSTR "/"
#define CALLBACK
#endif
#if defined(xdc_target__os_Linux)
#include <unistd.h>
#endif
#define XDCSHELF "packages" DIRSTR "xdc" DIRSTR "shelf" DIRSTR "java"
#define MAXJARS 32 /* maximum # of jars in class path */
#define MAXLINE 512 /* maximum interactive input line */
#define MAXNAME 512 /* maximum file name length */
#define MAXNUMPATH 16 /* max # of directories to search */
#define MAXOPTIONS 64 /* max options for JVM */
static Void error(Int exitStatus, String format, ...);
static String getCanonicalProgName(String argv0);
static String getLine(String prev);
static Bool getProgPath(Char *pathBuf);
static Void init(Void);
static Int initPathTab(String pathTab[]);
static void CALLBACK jvmAbort(void);
static void CALLBACK jvmExit(int status);
static Void printVersion(Void);
static Void printTime(String msg, struct timeb *t);
static Void usage(Void);
static String jarTab[MAXJARS + 1] = {
"js.jar", /* rhino JavaScript interpreter */
"config.jar", /* configuration model */
"antlr.jar", /* antlr parser used by iliad */
NULL /* NULL termination required */
};
static Int jarTabLen = 3;
static String jvmOptions[MAXOPTIONS + 1] = {
"-Xverify:none", /* disable *all* byte-code verification;
* this improves JVM startup time without
* affecting Java semantics
*/
NULL
};
static Int nOpts = 1;
static String pathTab[MAXNUMPATH + 1] = {NULL};
static Int pathTabLen = 0;
static Int dflag = 0;
static Bool iflag = FALSE;
static Bool mflag = FALSE;
static String progName = "";
/*
* ======== main ========
*/
int main(Int argc, String argv[])
{
Int i;
String errorMsg;
Int status = TCF_EOK;
TCF_Attrs attrs = TCF_ATTRS;
TCF_Handle tcf;
Bool xsFlags;
/* benchmark trace for Shell activated by -Dxdc.traceGroups="bench" */
Bool traceFlag;
String envtrace = NULL;
String xdcpath = NULL;
String xdcroot = NULL;
String progPath;
/* nargv is a list of arguments passed to Shell */
String *nargv;
String initScriptPath = NULL;
String xsRelativePath = "/packages/xdc/xs.js";
Int nargc = 0;
String err;
struct timeb startTime;
/* arguments for processing the optional ini file */
static String iniFileName = "/etc/xs.ini";
static Char iniFileOption[MAXNAME + 3] = "-@";
static String iniFilePath = &iniFileOption[2];
static String iniFileOptions[] = {
iniFileOption
};
/* We catch this time first, in case the benchmark trace is on */
ftime(&startTime);
progName = argv[0];
XUTL_init();
/* construct the ini file name */
if (getProgPath(iniFilePath)) {
strcat(iniFilePath, iniFileName);
/* if the ini file exists, add it to the command line */
if (XUTL_isFile(iniFilePath)) {
argv = XUTL_insertArgv(1, iniFileOptions, 1, argc, argv, &argc, &err);
if (argv == NULL) {
fprintf(stderr, "%s: unable to process ini file: %s\n\n",
progName, err);
exit(1);
}
}
}
/* expand any @file options */
argv = XUTL_expandArgv(argc, argv, &argc, &err);
if (argv == NULL) {
fprintf(stderr, "%s: unable to expand command line arguments: %s\n\n",
progName, err);
exit(1);
}
init();
/* The maximum number of pointers we may need is all of argv and three
* additional ones.
*/
nargv = (String *)malloc(sizeof(String) * (argc + 3));
if (nargv == NULL) {
error(1, "out of memory");
}
/* The path to the directory with this executable is needed to find
* the initial xs.js script and to pass xdc.root, which is required for
* the initial script.
*/
progPath = (String)malloc(MAXNAME + 1);
if (progPath == NULL) {
error(1, "out of memory");
}
getProgPath(progPath);
/* turn on/off trace */
GT_set("TC-016,GD-01");
/* initialize search path table and the root directory */
pathTabLen = initPathTab(pathTab);
attrs.pathTab = pathTab;
attrs.rootDir = pathTab[0];
attrs.jvmOpts = jvmOptions;
attrs.jarTab = jarTab;
attrs.jvmExit = jvmExit;
attrs.jvmAbort = jvmAbort;
/* parse command line args */
argv += 1; /* skip over xs executable name */
argc -= 1;
xsFlags = FALSE;
traceFlag = FALSE;
if (argc == 0) {
usage();
return (0);
}
while (argc > 0 && argv[0][0] == '-' && !xsFlags) {
switch (argv[0][1]) {
case 'h': {
usage();
return (0);
break;
}
case 'v': {
printVersion();
return(0);
break;
}
case 'p': {
/* add argv[1] to pathTab array (used to locate files) */
if (argc <= 1) {
usage();
exit(1);
}
if (pathTabLen >= MAXNUMPATH) {
error(0, "error: more than %d path names specified\n\n",
MAXNUMPATH);
for (i = 0; i < pathTabLen; i++) {
fprintf(stderr, " %d\t%s\n", i, pathTab[i]);
}
exit(1);
}
pathTab[pathTabLen++] = argv[1];
pathTab[pathTabLen] = NULL;
argv += 2;
argc -= 2;
break;
}
case 'D': {
/* If xdc.root is passed, we will not add our own */
if (strncmp(argv[0], "-Dxdc.root", 10) == 0) {
xdcroot = (String)malloc(strlen(argv[0]) - 10);
if (xdcroot == NULL) {
error(1, "out of memory");
}
strcpy(xdcroot, &argv[0][11]);
}
/* If xdc.traceGroups is passed, and 'bench' is in it, the
* trace option is passed to Shell.
*/
if (strncmp(argv[0], "-Dxdc.traceGroups", 17) == 0) {
if (strstr(argv[0] + 17, "bench") != NULL) {
traceFlag = TRUE;
}
}
jvmOptions[nOpts++] = argv[0];
if (nOpts >= MAXOPTIONS) {
error(1, "too many options for JVM\n\n");
}
argv += 1;
argc -= 1;
break;
}
case 'g': {
attrs.gflag = TRUE;
if (strncmp(argv[0], "-g=", 3) == 0) {
attrs.gopts = &(argv[0][3]);
}
argv += 1;
argc -= 1;
break;
}
case 'i': {
iflag = TRUE;
argv += 1;
argc -= 1;
break;
}
case '-' : {
switch (argv[0][2]) {
case 'h': {
if (strncmp(argv[0] + 2, "help", 4) == 0) {
usage();
return (0);
}
else {
usage();
error(1, "illegal option %s\n\n", argv[0]);
}
break;
}
case 'j': {
if (argc <= 1) {
usage();
exit(1);
}
if (jarTabLen >= MAXJARS) {
error(1, "too many jar files specified\n");
}
jarTab[jarTabLen++] = argv[1];
jarTab[jarTabLen] = NULL;
argv += 2;
argc -= 2;
break;
}
case 'v': {
if (strncmp(argv[0] + 2, "version", 7) == 0) {
printVersion();
return(0);
break;
}
else {
printf("illegal option %s\n\n", argv[0]);
usage();
exit(1);
}
}
case 'X': {
jvmOptions[nOpts++] = argv[0] + 1; /* skip extra '-' */
if (nOpts >= MAXOPTIONS) {
error(1, "too many options for JVM\n");
}
argv += 1;
argc -= 1;
break;
}
/* --xdcpath (or deprecated --xp) specifies XDCPATH */
case 'x': {
Int xpSize = 0;
if (strcmp(argv[0] + 2, "xdcpath") == 0 ||
strcmp(argv[0] + 2, "xp") == 0) {
if (argc > 1) {
xdcpath = argv[1];
argv += 2;
argc -= 2;
}
else {
usage();
error(1, "incomplete option %s\n\n", argv[0]);
}
break;
}
if (strncmp(argv[0], "--xdcpath=", 10) == 0) {
xpSize = 10;
}
else if (strncmp(argv[0], "--xp=", 5) == 0) {
xpSize = 5;
}
else {
usage();
error(1, "illegal option %s\n", argv[0]);
break;
}
xdcpath = argv[0] + xpSize;
argv += 1;
argc -= 1;
break;
}
case 'c': {
if (argv[0][3] != 'p') {
usage();
error(1, "illegal option %s\n", argv[0]);
}
if (argc <= 1) {
usage();
error(1, "incomplete option %s\n", argv[0]);
}
attrs.classPath = argv[1];
argv += 2;
argc -= 2;
break;
}
case 'd': {
dflag++;
GT_set("TC+016,GD+0123");
argv += 1;
argc -= 1;
break;
}
default:
printf("illegal option %s\n", argv[0]);
usage();
return (0);
break;
}
break;
}
case 'm':
case 'c':
case 'f': {
/* Any of these start xs.js arguments, which means that
* xs.c and JVM arguments all have to show up before these.
*/
xsFlags = TRUE;
break;
}
default: {
printf("illegal option %s\n", argv[0]);
usage();
return (0);
break;
}
}
}
/* Pass any command line specified xdcpath to JVM. If XDCPATH is not
* passed to JVM here, TCF_create will check if XDCPATH is defined in
* the environment and, if found, pass it to JVM.
*/
if (xdcpath != NULL) {
String def;
Int k, l;
/* Replace the consecutive semicolons by single semicolon */
for (k = 0, l = 0; xdcpath[k] != '\0'; k++) {
if (!((xdcpath[k] == ';') && (xdcpath[k+1] == ';'))) {
xdcpath[l] = xdcpath[k];
l++;
}
}
xdcpath[l] = '\0';
/* +16 for "-DXDCPATH=" */
def = (String)malloc(strlen(xdcpath) + 16);
if (def == NULL) {
error(1, "out of memory");
}
sprintf(def, "-DXDCPATH=%s", xdcpath);
/* add xdcpath to the jvmOptions */
jvmOptions[nOpts++] = def;
if (nOpts >= MAXOPTIONS) {
error(1, "too many options for JVM\n\n");
}
}
/* Handling of xdc.root has to be done in C code because it's used right
* at the beginning of JavaScript code. Also the path to xs executable
* is used to resolve xdc.root, so it is easier to handle
* everything here.
*
* If the user did not pass 'xdc.root' explicitly, we use the directory
* in which we found 'xs.c' to set 'xdc.root'.
*/
if (xdcroot == NULL) {
xdcroot = (String)malloc(strlen(progPath) + 1);
if (xdcroot == NULL) {
error(1, "out of memory");
}
strcpy(xdcroot, progPath);
/* add xdcroot to the jvmOptions */
jvmOptions[nOpts] = (String)malloc(strlen(xdcroot) + 13);
if (jvmOptions[nOpts] == NULL) {
error(1, "out of memory");
}
sprintf(jvmOptions[nOpts], "-Dxdc.root=%s", xdcroot);
nOpts += 1;
}
/* get the path to the initialization script */
initScriptPath = (String)malloc(strlen(xdcroot) +
strlen(xsRelativePath) + 2);
if (initScriptPath == NULL) {
error(1, "out of memory");
}
strcpy(initScriptPath, xdcroot);
strcat(initScriptPath, xsRelativePath);
/* create TCF session manager */
if ((tcf = TCF_create(NULL, &attrs, &errorMsg)) == NULL) {
error(1, "can't create session manager: %s\n", errorMsg);
}
if (iflag) {
nargv[nargc] = "-v";
nargc += 1;
}
if ((envtrace = getenv("XDC_TRACE_GROUPS")) != NULL) {
if (strstr(envtrace, "bench") != NULL) {
traceFlag = TRUE;
}
}
/* trace flag for Shell is turned on. */
if (traceFlag) {
printTime("xs.exe starts", &startTime);
fflush(NULL);
}
nargv[nargc] = initScriptPath;
nargc += 1;
if (argc == 0 && !iflag) {
/* No script, package or module is supplied and interactive mode is
* not requested.
*/
printf("Script, capsule, module or package must be supplied as an \n");
printf("argument,\nor interactive mode (-i) must be selected.\n");
usage();
return(0);
}
for (i = 0; i < argc; i++) {
nargv[nargc] = (String)malloc(strlen(argv[i]) + 1);
if (nargv[nargc] == NULL) {
error(1, "out of memory");
}
strcpy(nargv[nargc], argv[i]);
nargc += 1;
}
if (iflag) {
TCF_Session session;
session = TCF_start(tcf, nargc, nargv, &errorMsg);
if (session != NULL) {
String cmd = NULL;
while (status != TCF_EFATAL && (cmd = getLine(cmd)) != NULL) {
if ((status = TCF_process(session, cmd)) != TCF_EINCOMPLETE) {
free(cmd);
cmd = NULL;
}
}
TCF_stop(session);
}
else {
error(0, "can't create session: %s\n", errorMsg);
status = TCF_EINTERN;
}
}
else {
/* run TCF interpreter on the script name and get return status */
status = TCF_run(tcf, nargc, nargv);
}
free(xdcroot);
free(initScriptPath);
free(progPath);
/* we get here if main() returns without calling System.exit(); i.e.,
* exit status of 0 for the Rhino Shell. Otherwise the exit hook
* exits.
*/
if (dflag > 0) {
error(0, "exit status: %d\n", status);
}
/* delete interpreter and exit the TCF module */
TCF_delete(tcf);
TCF_exit();
if (traceFlag) {
printTime("xs.exe exits", NULL);
}
return (status);
}
/*
* ======== error ========
*/
static Void error(Int exitStatus, String format, ...)
{
va_list ap;
String canProgName = getCanonicalProgName(progName);
if (canProgName == NULL) {
canProgName = progName;
}
fprintf(stderr, "%s: %s", canProgName, exitStatus == 0 ? "" : "error: ");
va_start(ap, format);
(void)vfprintf(stderr, format, ap);
va_end(ap);
if (exitStatus != 0) {
exit(exitStatus);
}
}
/*
* ======== getLine ========
*/
static String getLine(String prev)
{
char buffer[MAXLINE + 1];
String prefix;
String command = NULL;
if (prev == NULL) {
fprintf(stdout, mflag ? ">>>>\n" : "js> ");
fflush(stdout);
prefix = "";
}
else {
prefix = prev;
}
if (fgets(buffer, sizeof(buffer), stdin)) {
if (strcmp(buffer, "quit\n") && strcmp(buffer, "quit\r\n")) {
Int len = strlen(buffer) + strlen(prefix);
if ((command = malloc(len + 1)) != NULL) {
strcpy(command, prefix);
strcat(command, buffer);
}
}
}
if (prev != NULL) {
free(prev);
}
return (command);
}
/*
* ======== getProgPath ========
* Return TRUE if a path to this executable, not including the name of the
* executable, is found and copied to pathBuf. Return FALSE if the path
* lookup failed.
*/
static Bool getProgPath(Char *pathBuf)
{
String tmp;
#if defined(xdc_target__os_Windows)
if (GetModuleFileName(0, pathBuf, MAXNAME) != 0) {
if ((tmp = strrchr(pathBuf, DIRSTR[0])) != NULL) {
tmp[0] = '\0';
return (TRUE);
}
}
pathBuf[0] = '\0'; /* just in case ... */
return (FALSE);
#else
if (strlen(progName) >= MAXNAME) {
pathBuf[0] = '\0';
return (FALSE);
}
strcpy(pathBuf, progName);
if ((tmp = strrchr(pathBuf, DIRSTR[0])) != NULL) {
tmp[0] = '\0';
}
else {
strcpy(pathBuf, ".");
}
/* if pathBuf is a relative path, prepend cwd to make absolute path */
if (pathBuf[0] != DIRSTR[0]) {
Char buf[MAXNAME + 1];
if (getcwd(buf, MAXNAME) != NULL) {
if ((strlen(pathBuf) + strlen(buf) + 2) < MAXNAME) {
if (strcmp(pathBuf, ".") != 0) {
strcat(buf, DIRSTR);
strcat(buf, pathBuf);
}
strcpy(pathBuf, buf);
}
}
}
return (TRUE);
#endif
}
/*
* ======== getCanonicalProgName ========
*/
static String getCanonicalProgName(String argv0)
{
#if defined(xdc_target__os_Windows)
if (strrchr(argv0, DIRSTR[0]) == NULL) {
Char buf[MAXNAME + 1];
String prog = NULL;
if (getProgPath(buf)) {
if ((strlen(buf) + strlen(argv0)) < MAXNAME) {
strcat(buf, DIRSTR);
strcat(buf, argv0);
if ((prog = malloc(strlen(buf) + 1)) != NULL) {
strcpy(prog, buf);
}
}
}
return (prog);
}
#endif
return (XUTL_getCanonicalPath(argv0));
}
/*
* ======== init ========
*/
static Void init(Void)
{
TCF_init();
GT_init();
#if 0
/* It now looks like we don't need to hack below: detaching the
* main thread in TCF_delete() seems to prevent the deadlock
* described below. We keep the code and description just in case ...
*/
#if defined(xdc_target__os_Windows)
/*
* HACK!!! BUG: the following line causes this utility to only run on
* a single-processor of multi-processor NT system. We do this to avoid
* what is an apparent deadlock that occurs between the Java garbage
* collector and this thread:
*
* garbage collector:
* calls free()
* free() acquires heap lock
* -----> preempted by xs main thread
*
* xs:
* calls TCF_delete()
* calls JVM destroy
* JVM destroy suspends garbage collector
* calls free() (for TCF object)
* free blocks on heap lock owned by suspended gc
*/
SetProcessAffinityMask(GetCurrentProcess(), 0x1);
#endif
#endif
}
/*
* ======== initPathTab ========
* Initialize the path table pathTab. This path table is used to
* search for the loadable JVM, as well as the JavaScript jar (js.jar)
* and the configuration jar (config.jar).
*
* The search path is initialized as follows:
* dirname(argv[0])
* dirname(argv[0])/XDCSHELF
*/
static Int initPathTab(String pathTab[])
{
static Char appBuf[MAXNAME + 1]; /* dirname(argv[0]) */
static Char shelfBuf[MAXNAME + 1]; /* dirname(argv[0])/XDCSHELF */
Int i = 0;
if (getProgPath(appBuf)) {
/* The path added here is where JRE is found in XDCtools product. */
pathTab[i++] = appBuf;
/* In some cases, we may want to add JRE to the shelf. */
if ((strlen(appBuf) + strlen(XDCSHELF) + 1) < MAXNAME) {
strcpy(shelfBuf, appBuf);
strcat(shelfBuf, DIRSTR XDCSHELF);
pathTab[i++] = shelfBuf;
}
}
/* null terminate the path table */
pathTab[i] = NULL;
if (i > MAXNUMPATH) {
error(1, "too many path names specified\n");
}
return (i);
}
/*
* ======== jvmAbort ========
* Called by JVM on abort
*/
static void CALLBACK jvmAbort(void)
{
if (dflag > 0) {
error(0, "JVM abort hook\n");
}
abort();
}
/*
* ======== jvmExit ========
* Called by JVM on exit.
*/
static void CALLBACK jvmExit(int status)
{
if (dflag > 0) {
error(status, "JVM exit: %d\n", status);
}
TCF_exit();
exit(status);
}
/* the following three definitions are required for ident.c */
#define _NAME_ __FILE__
#define _DATE_ __DATE__
#define _CSUM_ ""
#include "../../../../ident.c" /* defines _VERS_ string */
/*
* ======== printTime ========
*
* print the message and the time passed in the second argument, except when
* that argument is NULL. In that case, the current time is printed.
*/
static Void printTime(String msg, struct timeb *t)
{
struct timeb tb;
struct tm *btime;
char *wmsg;
int wmsg_size = 9; // hh:mm:ss
char *pmsg;
int pmsg_size = 3; // am/pm
wmsg = (char *)malloc(sizeof(char) * wmsg_size);
pmsg = (char *)malloc(sizeof(char) * pmsg_size);
if (wmsg == NULL || pmsg == NULL) {
error(1, "out of memory");
}
if (t == NULL) {
ftime(&tb);
t = &tb;
}
btime = localtime(&(t->time));
strftime(wmsg, wmsg_size, "%I:%M:%S", btime);
strftime(pmsg, pmsg_size, "%p", btime);
printf("TIME=%s.%03u %s %s\n", wmsg, t->millitm, pmsg, msg);
free(pmsg);
free(wmsg);
}
/*
* ======== printVersion ========
*/
static Void printVersion(Void)
{
Char *vers = __VERS;
if (vers[0] == '@') {
while (vers[0] != '\0' && !isspace(vers[0])) {
vers++;
}
while (vers[0] != '\0' && isspace(vers[0])) {
vers++;
}
}
/* don't use getProgPath(); the output of this function should be
* independent of the caller's environment
*/
printf("xs (XDCscript Interpreter) %s, %s\n", vers, __DATE);
}
/*
* ======== usage ========
*/
static Void usage(Void)
{
printf("Usage: xs [options] [<script> | <capsule> | <module> | <package>] [args]\n");
printf("Options:\n");
printf(" -h, --help\t\tPrint this message and exit\n");
printf(" -v, --version\t\tPrint the version and exit\n");
printf(" @opts-file\t\tRead the specified file and include its contents ");
printf("as\n\t\t\targuments to xs\n");
/*printf("[--cp <classpath>]\n\t [--lp <java library path>] ");*/
printf(" -i\t\t\tRun in interactive mode\n");
printf(" --xdcpath PATH, or\n --xdcpath=PATH");
printf("\tAdd PATH to the beginning of the package path\n");
/*printf(" --X<opt>\t\tOption -X<opt> is passed directly to SUN JVM\n");*/
printf(" -D<name>=<val>\tAdd the \"name=value\" definition to the XDC\n");
printf("\t\t\tScript environment\n");
printf(" -g[=<opts>]\t\tuse graphical debugger to run script, and \n");
printf("\t\t\toptionally pass <opts> to debugger\n");
printf("\t\t\tThis option must be last in [options].\n\n");
printf(" -m\t\t\tSpecifies that the next argument is the name of\n");
printf("\t\t\ta module. The module must define a main() function.\n");
printf("\t\t\tThis option must be last in [options].\n");
printf(" -f\t\t\tSpecifies that the next argument is the name of\n");
printf("\t\t\ta script. This option must be last in [options].\n");
printf(" -c\t\t\tSpecifies that the next argument is the name of\n");
printf("\t\t\ta capsule. The module must define a main() function.\n");
printf("\t\t\tThis option must be last in [options].\n\n");
printf(" If none of the options '-m', '-f', and '-c' is specified, \n");
printf(" the argument given to xs after [options] is assumed to\n");
printf(" be a package name. This package must have a module named\n");
printf(" Main that defines a main() function.\n\n");
printf("Graphical Debugger Options:\n");
printf(" -i\t\t\tbreak at the first line of the startup script\n\n");
}