Summary
This file contains 4 classes:
ArgumentExpander
which reads the command line arguments and stores them.
It handles @file redirection so arguments can be in files.
KeywordMap
simple struct class that holds keyword information.
OptionError
simple class to store bad option information.
Option
storage class that reads the command line arguments from an
ArgumentExpander class and sets class-public booleans.
Source
// $Id: option.cpp,v 1.7 1999/03/10 19:59:21 shields Exp $
copyright notice
#include <ctype.h>
#include "config.h"
#include "option.h"
#include "javasym.h"
#include "error.h"
ArgumentExpanded will interpret it's second argument as a filename and
will append arguments found in the file to the first argument. If the
second argument is NOT a file then this will return false.
bool ArgumentExpander::ArgumentExpanded(Tuple<char *> &arguments, char *file_name)
{
struct stat status;
FILE *afile = fopen(file_name, "r");
if (afile && (::SystemStat(file_name, &status) == 0))
{
char *buffer = new char[status.st_size + 2];
int file_size = fread(buffer, 1, status.st_size, afile);
buffer[file_size] = '\n';
for (int k = 0; k < file_size; k++)
{
//
// isgraph(c) is true if c is any printing character except space.
//
while ((! isgraph(buffer[k])) && buffer[k] != '\n' && buffer[k] != '\r')
k++;
if (buffer[k] != '\n' && buffer[k] != '\r')
{
int n;
for (n = k + 1; buffer[n] != '\n' && buffer[n] != '\r'; n++)
;
buffer[n] = U_NULL;
char *str = new char[n - k + 1];
strcpy(str, &buffer[k]);
arguments.Next() = str;
k = n;
}
}
delete [] buffer;
fclose(afile);
return true;
}
return false;
}
ArgumentExpander is the primary constructor that processes command line
arguments. An example of use is:
ArgumentExpander *arguments = new ArgumentExpander(argc, argv);
This will eventually be passed to the Option class constructor.
ArgumentExpander::ArgumentExpander(int argc_, char *argv_[])
{
Tuple<char *> arguments(8192);
for (int i = 0; i < argc_; i++)
{
char *argument = argv_[i];
if (argument[0] != '@' || (! ArgumentExpanded(arguments, argument + 1)))
{ If the argument is not a file redirection (i.e. @file)
char *str = new char[strlen(argument) + 1];
strcpy(str, argument);
arguments.Next() = str;
}
}
argc = arguments.Length();
argv = new char*[argc];
for (int k = 0; k < argc; k++)
argv[k] = arguments[k];
return;
}
ArgumentExpander can be used internally to parse a line that contains
command line style arguments. This would be useful for constructing argument
lists for further function calls.
ArgumentExpander::ArgumentExpander(Tuple<char> &line)
{
Tuple<char *> arguments(8192);
int end = 0;
do
{
for (; end < line.Length() && line[end] == U_SPACE; end++)
;
if (end < line.Length())
{
int start = end;
for (end++; end < line.Length() && line[end] != U_SPACE; end++)
;
int length = end - start;
char *argument = new char[length + 1];
for (int i = 0, k = start; k < end; i++, k++)
argument[i] = line[k];
argument[length] = U_NULL;
if (argument[0] == '@' && ArgumentExpanded(arguments, argument + 1))
delete argument;
else arguments.Next() = argument;
}
} while(end < line.Length());
argc = arguments.Length();
argv = new char*[argc];
for (int k = 0; k < argc; k++)
argv[k] = arguments[k];
return;
}
#ifdef WIN32_FILE_SYSTEM
void Option::SaveCurrentDirectoryOnDisk(char c)
{
if (! current_directory[c])
{
char *disk_directory = NULL,
disk[3] = { c, U_COLON, U_NULL },
tmp[1];
if (SetCurrentDirectory(disk))
{
DWORD directory_length = GetCurrentDirectory(0, tmp); // first, get the right size
disk_directory = new char[directory_length + 1]; // allocate the directory
DWORD length = GetCurrentDirectory(directory_length, disk_directory);
if (length <= directory_length)
{
for (char *ptr = disk_directory; *ptr; ptr++)
*ptr = (*ptr != U_BACKSLASH ? *ptr : (char) U_SLASH); // turn '\' to '/'.
}
}
if (! disk_directory)
{If we failed to set the disk_directory, make it "."
disk_directory = new char[2];
strcpy(disk_directory, StringConstant::U8S__DO_);
}
current_directory[Case::ToAsciiLower(c)] = disk_directory;
current_directory[Case::ToAsciiUpper(c)] = disk_directory;
}
return;
}
#endif
Option::Option(ArgumentExpander &arguments) : default_path(NULL),
classpath(NULL),
makefile_name(NULL),
debug_dump_lex(false),
debug_dump_ast(false),
debug_dump_class(false),
debug_trap_op(false),
applet_author(false),
incremental(false),
makefile(false),
bytecode(true),
full_check(false),
unzip(false),
dump_errors(false),
errors(true),
ascii(false),
comments(false),
pedantic(false),
directory(NULL),
first_file_index(arguments.argc),
one_one(true),
g(false),
nowrite(false),
deprecation(false),
verbose(false),
depend(false),
nowarn(false),
O(false),
zero_defect(false)
{
The WIN32_FILE_SYSTEM has the notion of logical drives, each with it's
own drive letter from a thru z. In a fully expanded path the drive letter
is the first character of the path, followed by a colon. The path is a
series of symbols, possibly containing spaces, separated by backslashes thus:
C:\Java\bin\java.exe
Note that case is ignored.
#ifdef WIN32_FILE_SYSTEM
current_directory is an array of the current directory on each logical
disk in a windows file system. First we make sure they are all null.
for (int j = 0; j < 128; j++)
current_directory[j] = NULL;
We need to dance a bit to get the current directory for current logical
disk at the time we are invoked. First we get the length of the directory
by asking for it using a zero length buffer. Then we allocate a string to
hold the result and we ask again, saving the result in main_current_directory.
char tmp[1];
DWORD directory_length = GetCurrentDirectory(0, tmp); // first, get the right size
char *main_current_directory = new char[directory_length + 1]; // allocate the directory
DWORD length = GetCurrentDirectory(directory_length, main_current_directory);
if (length > directory_length)
{ the directory doesn't exist
delete [] main_current_directory;
main_current_directory = StringConstant::U8S__DO_;
main_disk = 0;
}
else
{
Now that we have the directory name we fix the backslashes. Then we chew
off the first character, which in windows is the logical drive letter and
call it main_disk. main_disk is used as an index for the current_directory
array. We store it twice, once using the lower case drive letter as an index
and once using the upper case letter as an index (type crufty but it works).
for (char *ptr = main_current_directory; *ptr; ptr++)
*ptr = (*ptr != U_BACKSLASH ? *ptr : (char) U_SLASH); // turn '\' to '/'.
main_disk = main_current_directory[0]; // the first character
current_directory[Case::ToAsciiLower(main_disk)] = main_current_directory;
current_directory[Case::ToAsciiUpper(main_disk)] = main_current_directory;
}
Finally we store the same information as the zero-th entry in the
current_directory array. Note that if main_disk is zero then that is
NOT used as a current_directory index but is considered a flag that the
main_disk and main_current_directory are invalid.
current_directory[0] = main_current_directory;
#endif
Tuple<int> filename_index(2048);
arguments is a class object of type ArgumentExpander. This class is a
container for the argument list given to main. argc is the number of strings
and argv is an array of strings.
for (int i = 1; i < arguments.argc; i++)
{
For each string in the array of arguments it must be one of three cases.
case 1: It has a - prefix. It is a standard option, process it.
case 2: It has a + prefix. This is jikes specific, process it.
case 3: It must be a filename.
processing the option usually sets a boolean flag. If the option has a
parameter associated with it (e.g. -classpath) we process the next string
as the parameter and increment the loop index inside the loop.
if (arguments.argv[i][0] == '-')
{
case 1: It has a - prefix
if (strcmp(arguments.argv[i],"-classpath") == 0 && ((i + 1) < arguments.argc))
{classpath as a command-line option will override both the
JIKESPATH environment variable and the CLASSPATH environment
variable. It was defaulted to NULL on the method signature line.
classpath = arguments.argv[++i];
#ifdef EBCDIC
EBCDIC is a mainframe code similar to ASCII. Like IPLs and
VTOCs only IBMers know the whys and wherefores of EBCDIC.
//
// Maintain CLASSPATH in ASCII and translate back to EBCDIC when building file name
//
for (int k = 0; k < strlen(classpath); k++)
classpath[k] = Code::ToASCII(classpath[k]);
#endif
}
else if (strcmp(arguments.argv[i], "-depend") == 0)
We want to recompile all of the classes these classes depend on
depend = true;
else if (strcmp(arguments.argv[i],"-verbose") == 0)
We want to list all of the files read and written.
verbose = true;
else if (strcmp(arguments.argv[i],"-g") == 0)
generate LocalVariableTable attribute (for debugging)
g = true;
else if (strcmp(arguments.argv[i], "-O") == 0)
do not generate LineNumberTable attribute
O = true;
else if (strcmp(arguments.argv[i],"-deprecation") == 0)
{ We want complaints about Deprecated features (not implemented)
bad_options.Next() = new OptionError(SemanticError::UNSUPPORTED_OPTION, arguments.argv[i]);
deprecation = true;
}
else if (strcmp(arguments.argv[i],"-nowrite") == 0)
We don't want to write the class files
nowrite = true;
else if (strcmp(arguments.argv[i],"-nowarn") == 0)
We want to suppress warning messages.
nowarn = true;
else if (strcmp(arguments.argv[i], "-d") == 0 && ((i + 1) < arguments.argc))
{We want to write class files to the specified directory
++i;
#ifdef UNIX_FILE_SYSTEM
in UNIX remember the current directory
int length = strlen(arguments.argv[i]);
directory = new char[length + 1];
strcpy(directory, arguments.argv[i]);
#elif defined(WIN32_FILE_SYSTEM)
If the directory is prefixed by a new logical disk letter we need to update
our table of current directories for the logical disk and make windows
aware that we want a new logical directory on the new logical drive.
char disk = (strlen(arguments.argv[i]) >= 2 &&
Case::IsAsciiAlpha(arguments.argv[i][0]) &&
arguments.argv[i][1] == U_COLON
? arguments.argv[i][0]
: 0);
Save the directory where we currently are pointing
SaveCurrentDirectoryOnDisk(disk);
if (SetCurrentDirectory(arguments.argv[i]))
{Move to the new directory and save it
char tmp[1];
DWORD directory_length = GetCurrentDirectory(0, tmp); // first, get the right size
directory = new char[directory_length + 1]; // allocate the directory
DWORD length = GetCurrentDirectory(directory_length, directory);
if (length > directory_length)
{ the directory doesn't exist
delete [] directory;
directory = NULL;
}
}
Let windows know the current directory for the new logical disk
ResetCurrentDirectoryOnDisk(disk); // reset the current directory on the disk
And go back to the main logical disk again
SetMainCurrentDirectory(); // reset the real current directory...
if (! directory)
bad_options.Next() = new OptionError(SemanticError::INVALID_DIRECTORY, arguments.argv[i]);
#endif
#ifdef EBCDIC
NOTE: if EBCDIC and WIN32_FILE_SYSTEM get defined together this code will
fail because directory[k] will contain backslashes. This implies that the
two switches are mutually exclusive. Heaven forfend anyone should run
windows on an EBCDIC machine.
//
// need to translate directory name to ASCII
//
for (int k = 0; k < directory_length; k++)
directory[k] = Code::ToASCII(directory[k]);
#endif
if (directory)
{Replace the backslashes. Code should be moved up?
for (char *ptr = directory; *ptr; ptr++)
*ptr = (*ptr != U_BACKSLASH ? *ptr : (char) U_SLASH); // turn '\' to '/'.
}
}
else bad_options.Next() = new OptionError(SemanticError::INVALID_OPTION, arguments.argv[i]);
Somebody gave us an option prefixed with - we don't recognize
}
else if (arguments.argv[i][0] == '+')
{
case 2: It has a + prefix
if (strcmp(arguments.argv[i], "+AA") == 0)
applet_author = true;
if (strcmp(arguments.argv[i], "+A") == 0)
debug_dump_ast = true;
#ifdef EBCDIC
else if (strcmp(arguments.argv[i], "+ASCII") == 0)
ascii = true;
#endif
else if (strcmp(arguments.argv[i], "+B") == 0)
do not generate byte codes. skips a compiler step
bytecode = false;
else if (strcmp(arguments.argv[i], "+c") == 0)
comments = true;
else if (strcmp(arguments.argv[i], "+C") == 0)
debug_dump_class = true;
else if (strcmp(arguments.argv[i],"+D") == 0)
{ List errors as they happen. Don't wait for compile complete
dump_errors = true;
use Emacs error message form (+E option forced)
errors = false;
}
else if (strcmp(arguments.argv[i],"+E") == 0)
use Emacs error message form
errors = false;
else if (arguments.argv[i][0] == '+' && arguments.argv[i][1] == 'K')
{add the symbol to the keyword list.
(I don't understand why this option exists -- tpd)
char *name = arguments.argv[i] + 2,
*image;
for (image = name; *image && *image != '='; image++)
;
if (*image != '=')
bad_options.Next() = new OptionError(SemanticError::INVALID_K_OPTION, arguments.argv[i]);
else
{
int key = 0; // assume undefined
image++;
if (strcmp(image, "boolean") == 0)
key = TK_boolean;
else if (strcmp(image, "byte") == 0)
key = TK_byte;
else if (strcmp(image, "char") == 0)
key = TK_char;
else if (strcmp(image, "short") == 0)
key = TK_short;
else if (strcmp(image, "int") == 0)
key = TK_int;
else if (strcmp(image, "long") == 0)
key = TK_long;
else if (strcmp(image, "float") == 0)
key = TK_float;
else if (strcmp(image, "double") == 0)
key = TK_double;
else bad_options.Next() = new OptionError(SemanticError::INVALID_K_TARGET, image);
if (key != 0)
{
int i = keyword_map.NextIndex();
keyword_map[i].key = key;
keyword_map[i].length = image - name - 1;
keyword_map[i].name = new wchar_t[keyword_map[i].length];
for (int k = 0; k < keyword_map[i].length; k++)
keyword_map[i].name[k] = name[k];
}
}
}
else if (strcmp(arguments.argv[i],"+1.0") == 0)
recognize only the 1.0 level of the language, reporting as errors
any uses of features added in the 1.1 level.
one_one = false;
else if (strcmp(arguments.argv[i],"+F") == 0)
do full dependence check except for Zip and Jar files
full_check = true;
else if (strcmp(arguments.argv[i],"+M") == 0)
{generate makefiles with dependencies
makefile = true;
do full dependence check except for Zip and Jar files
full_check = true;
}
else if (strncmp(arguments.argv[i], "+M=", 3) == 0)
{generate makefiles with dependencies
makefile = true;
do full dependence check except for Zip and Jar files
full_check = true;
makefile_name = &arguments.argv[i][3];
}
else if (strcmp(arguments.argv[i], "+O") == 0)
{
debug_trap_op = atoi(arguments.argv[i+1]);
i++;
}
else if (strcmp(arguments.argv[i],"+P") == 0)
generate pedantic listing
pedantic = true;
else if (arguments.argv[i][0] == U_PLUS && arguments.argv[i][1] == U_T)
{set the tab size in listings (tabs are evil -- tpd)
int tab_size = 0;
char *image = arguments.argv[i] + 2,
*p;
for (p = image; *p && Code::IsDigit(*p); p++)
{
int digit = *p - U_0;
tab_size = tab_size * 10 + digit;
}
if (*p)
bad_options.Next() = new OptionError(SemanticError::INVALID_TAB_VALUE, image);
Tab::SetTabSize(tab_size == 0 ? Tab::DEFAULT_TAB_SIZE : tab_size);
}
else if (strcmp(arguments.[i], "+L") == 0)
debug_dump_lex = true;
else if (strcmp(arguments.argv[i],"+U") == 0)
{
unzip = true;
do full dependence check except for Zip and Jar files
full_check = true;
}
else if (strcmp(arguments.argv[i],"++") == 0)
{We want to keep the compiler running continuously
incremental = true;
do full dependence check except for Zip and Jar files
full_check = true;
}
else if (strcmp(arguments.argv[i], "+Z") == 0)
treat cautions as errors
zero_defect = true;
else bad_options.Next() = new OptionError(SemanticError::INVALID_OPTION, arguments.argv[i]);
}
case 3: It is a filename
else filename_index.Next() = i;
}
classpath is set to:
if specified on the command line by -classpath, use that
else if JIKESPATH exists in the environment, use that
else if CLASSPATH exists in the environment, use that
else classpath is NULL.
if (! classpath)
{
classpath = getenv("JIKESPATH");
if (! classpath)
classpath = getenv("CLASSPATH");
if (classpath)
{
#ifdef EBCDIC
//
// Maintain CLASSPATH in ASCII and translate back to EBCDIC when building file name
//
for (int k = 0; k < strlen(classpath); k++)
classpath[k] = Code::ToASCII(classpath[k]);
#endif
while (isspace(*classpath))
classpath++;
if (*classpath == U_NULL)
classpath = NULL;
}
if (! classpath)
{
default_path = new char[2];
default_path[0] = '.';
default_path[1] = U_NULL;
classpath = default_path;
}
}
//
// Initially, first_file_index is set to argc. Since the array filename_index
// contains the indices of all the input files in arguments in reverse order,
// we reverse the order here again to get the original list...
//
for (int k = filename_index.Length() - 1; k >= 0; k--)
{
first_file_index--;
int i = filename_index[k];
char *temp = arguments.argv[i];
arguments.argv[i] = arguments.argv[first_file_index];
arguments.argv[first_file_index] = temp;
}
return;
}
Option::~Option()
{
for (int i = 0; i < bad_options.Length(); i++)
delete bad_options[i];
delete [] default_path;
delete [] directory;
#ifdef WIN32_FILE_SYSTEM
NOTE: doesn't delete all 128 entries. why?
for (char c = 'a'; c <= 'z'; c++)
delete [] current_directory[c];
#endif
return;
}