cfad47cfa3/t3compiler/tads3/tccmdutl.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 11.8 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
/*
* Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
tccmdutl.cpp - TADS 3 Compiler command-line parsing utilities
Function
Defines some utility functions for command-line parsing.
Notes
Modified
04/03/02 MJRoberts - Creation
*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "os.h"
#include "t3std.h"
#include "tccmdutl.h"
/* ------------------------------------------------------------------------ */
/*
* Get an option argument. We take a argument vector, the index of the
* vector entry containing the option whose argument we're to fetch, and
* the length of the option string. We'll return a pointer to the option
* string, or null if no argument is present.
*
* We'll look for the argument's option first in the same vector entry
* containing the option, appended directly to the option string; if
* there's nothing there, we'll look for the argument in the next vector
* entry. This lets us find option arguments that use syntax like
* "-Itest" or "-I test".
*/
char *CTcCommandUtil::get_opt_arg(int argc, char **argv,
int *curarg, int optlen)
{
/*
* if it's jammed up against the option letter, get it from the
* current array entry; otherwise, get it from the next array entry
*/
if (argv[*curarg][optlen + 1] != '\0')
{
/* it's attached to the same option entry */
return argv[*curarg] + optlen + 1;
}
else if (*curarg + 1 < argc)
{
/* it's by itself as the next option entry */
++(*curarg);
return argv[*curarg];
}
else
{
/* it's not present at all */
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* Simple option helper implementation for counting arguments. Some
* callers will want to let us scan an option file to get an argument count
* before they allocate space for the arguments. On these count-only
* passes, the caller doesn't usually care about the contents of the file,
* so they don't want to bother performing the full set of operations that
* the helper normally defines. For these cases, callers can pass us a
* null helper, in which case we'll use this simple helper by default.
*/
class CTcOptFileHelperDefault: public CTcOptFileHelper
{
public:
/* allocate an option string */
virtual char *alloc_opt_file_str(size_t len)
{
return (char *)t3malloc(len + 1);
}
/* free a string allocated with alloc_opt_file_str() */
virtual void free_opt_file_str(char *str)
{
t3free(str);
}
/* process a comment line */
virtual void process_comment_line(const char *) { }
/* process a non-comment line */
virtual void process_non_comment_line(const char *) { }
/* process a configuration line */
virtual void process_config_line(const char *, const char *, int) { }
};
/* ------------------------------------------------------------------------ */
/*
* Parse an options file. If argv is null, we'll simply count the
* arguments and return the count. If argv is non-null, it must be
* allocated with the proper number of slots for the number of arguments
* in the file (as obtained from a call to this function with argv = null).
*
* Each string returned in argv[] is separately allocated with a call to
* helper->alloc_opt_file_str().
*/
int CTcCommandUtil::parse_opt_file(osfildef *fp, char **argv,
CTcOptFileHelper *helper)
{
char *buf;
char config_id[128];
size_t buflen;
int argc;
CTcOptFileHelperDefault default_helper;
/* if they didn't give us a helper object, use our default */
if (helper == 0)
helper = &default_helper;
/* allocate our initial buffer */
buflen = 512;
buf = helper->alloc_opt_file_str(buflen);
/* we're not in a configuration section yet */
config_id[0] = '\0';
/* keep going until we run out of text in the file */
for (argc = 0 ; ; )
{
size_t len;
char *p;
/* read the next line - stop if we're done */
if (osfgets(buf, buflen, fp) == 0)
break;
/* check for proper termination */
for (;;)
{
/* get the buffer length */
len = strlen(buf);
/* check for a newline of some kind at the end */
if (len != 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
{
/*
* remove consecutive newline/return characters - in case
* we are using a file moved from another system with
* incompatible newline conventions, simply remove all of
* the newlines we find
*/
while (len != 0
&& (buf[len-1] == '\n' || buf[len - 1] == '\r'))
--len;
/* null-terminate the line */
buf[len] = '\0';
/* we now have our line */
break;
}
else if (len == buflen - 1)
{
char *new_buf;
size_t new_buf_len;
/*
* The buffer was completely filled, and wasn't
* null-terminated - the line must be too long for the
* buffer. Expand the buffer and go back for more.
*/
new_buf_len = buflen + 512;
new_buf = helper->alloc_opt_file_str(new_buf_len);
/* copy the old buffer into the new buffer */
memcpy(new_buf, buf, buflen);
/* discard the old buffer */
helper->free_opt_file_str(buf);
/* switch to the new buffer */
buf = new_buf;
buflen = new_buf_len;
/* append more data into the line buffer and try again */
if (osfgets(buf + len, buflen - len, fp) == 0)
break;
}
else
{
/*
* The buffer wasn't completely full, and we didn't find a
* newline. This must mean that we've reached the end of
* the file, so we've read as much as we can for this
* line.
*/
break;
}
}
/* skip leading spaces */
for (p = buf ; isspace(*p) ; ++p) ;
/*
* Check to see if we're entering a new configuration section. If
* the line matches the pattern "[Config:xxx]", then we're starting
* a new configuration section with identifier "xxx".
*/
if (strlen(p) > 8 && memicmp(p, "[config:", 8) == 0)
{
char *start;
size_t copy_len;
/*
* note the part after the colon and up to the closing bracket
* - it's the ID of this configuration section
*/
for (p += 8, start = p ; *p != ']' && *p != '\0' ; ++p) ;
/* limit the ID size to our buffer maximum */
copy_len = p - start;
if (copy_len > sizeof(config_id) - 1)
copy_len = sizeof(config_id) - 1;
/* copy and null-terminate the ID string */
memcpy(config_id, start, copy_len);
config_id[copy_len] = '\0';
/* process the configuration ID line itself as a config line */
helper->process_config_line(config_id, buf, TRUE);
/* we're done processing this line */
continue;
}
/*
* if we're in a configuration section, simply process the line
* through the helper as a configuration line - this doesn't
* contain any options we can parse, since configuration sections
* are private to their respective definers
*/
if (config_id[0] != '\0')
{
/* process the configuration line through the helper */
helper->process_config_line(config_id, buf, FALSE);
/* we're done with this line */
continue;
}
/*
* If we've reached the end of the line or a command character
* ('#'), we're done with this line. Note that we check for
* comments AFTER checking to see if we're in a config section,
* because we want to send any comment lines within a config
* section to the helper for processing as part of the config -
* even comments are opaque to us within a config section.
*/
if (*p == '\0' || *p == '#')
{
/* tell the helper about the comment */
helper->process_comment_line(buf);
/* we're done with the line - go back for the next one */
continue;
}
/* tell the helper about the non-comment line */
helper->process_non_comment_line(buf);
/* parse the options on this line */
for (p = buf ; *p != '\0' ; )
{
char *start;
size_t arg_len;
/* skip leading spaces */
for ( ; isspace(*p) ; ++p) ;
/* check to see if we reached the end of the line */
if (*p == '\0')
break;
/*
* if the argument is quoted, find the matching quote;
* otherwise, look for the next space
*/
if (*p == '"')
{
char *dst;
/* find the matching quote */
for (++p, start = dst = p ; *p != '\0' ; ++p)
{
/*
* if this is a quote, check for stuttering - if it's
* stuttered, treat it as a single quote (and convert
* it to same), otherwise we've reached the end of the
* argument
*/
if (*p == '"')
{
/* check for stuttering */
if (*(p+1) == '"')
{
/*
* it's stuttered - skip the extra quote, so
* that we copy only a single quote
*/
++p;
}
else
{
/* this is our closing quote - skip it */
++p;
/* we're done with the argument */
break;
}
}
/* copy this character to the output */
*dst++ = *p;
}
/* note the length of the argument */
arg_len = dst - start;
}
else
{
/* find the next space */
for (start = p ; *p != '\0' && !isspace(*p) ; ++p) ;
/* note the length of the argument */
arg_len = p - start;
}
/* store the argument if we have an output vector */
if (argv != 0)
{
/* allocate the argument */
argv[argc] = helper->alloc_opt_file_str(arg_len + 1);
/* copy it into the result */
memcpy(argv[argc], start, arg_len);
argv[argc][arg_len] = '\0';
}
/* count the argument */
++argc;
}
}
/* done with our line buffer - delete it */
helper->free_opt_file_str(buf);
/* return the argument count */
return argc;
}
|