/*---------------------------------------------------------------------------
* LPMud Driver main module.
*
*---------------------------------------------------------------------------
* Here is the main() function which parses the commandline arguments,
* initializes everything and starts the backend loop. A documentation
* of the available commandline arguments is in the file
* doc/driver/invocation; the argument '--help' causes the driver to
* print a short help to all available arguments.
*
* The commandline arguments are actually parsed twice, since some
* arguments expect a functional master object (like the 'f' argument).
*
* The argument parser is an adaption of a generic parser. All the associated
* code and comments is kept in the lower half of this source for better
* readability.
*
* This file also contains several global functions and variables, some of
* which should probably go into a dedicated 'global' module or somewhere else
* appropriate.
* TODO: Move out all those variables and functions which are illfitting here.
* TODO: Put the argument parsing into a separate file?
*---------------------------------------------------------------------------
*/
#include "driver.h"
#include "typedefs.h"
#include "my-alloca.h"
#include <ctype.h>
#include <fcntl.h>
#include <locale.h>
#include <math.h>
#ifdef HAVE_NETDB_H
# include <netdb.h>
/* MAXHOSTNAMELEN on Solaris */
#endif
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef NeXT
#include <sys/param.h>
#endif
#if defined(AMIGA) && !defined(__GNUC__)
#include "hosts/amiga/socket.h"
#endif
#define NO_REF_STRING
#include "main.h"
#include "backend.h"
#include "array.h"
#include "comm.h"
#include "filestat.h"
#include "gcollect.h"
#include "interpret.h"
#include "lex.h"
#include "mapping.h"
#include "object.h"
#include "otable.h"
#include "patchlevel.h"
#include "random.h"
#include "rxcache.h"
#include "simulate.h"
#include "simul_efun.h"
#include "stdstrings.h"
#include "stralloc.h"
#include "svalue.h"
#include "swap.h"
#include "wiz_list.h"
#include "xalloc.h"
/*-------------------------------------------------------------------------*/
#define PLAIN_MASTER "secure/master"
#define COMPAT_MASTER "obj/master"
/*-------------------------------------------------------------------------*/
/* -- Pure commandline arguments -- */
int d_flag = 0; /* Debuglevel */
/* TODO: Make this bitflags, one for 'trace refcounts' etc */
Bool t_flag = MY_FALSE; /* True: Disable heart beat and reset */
static int e_flag = MY_FALSE; /* Passed to preload(), usually disables it */
Bool comp_flag = MY_FALSE; /* Trace compilations */
#ifdef DEBUG
Bool check_a_lot_ref_counts_flag = MY_FALSE; /* The name says it. */
int check_state_level = 0; /* how of to check the state in the loop */
#endif
#ifdef CHECK_STRINGS
Bool check_string_table_flag = MY_FALSE;
#endif
Bool strict_euids = MY_FALSE; /* Enforce use of the euids */
static uint32 random_seed = 0; /* The seed for the pseudo-random generator. */
static char * hostname = NULL;
static char * hostaddr = NULL;
/* Hostname and -addr given on the commandline. They are passed as
* arguments to initialize_host_ip() and are then no longer used.
*/
/* -- Configuration options -- */
long time_to_reset = TIME_TO_RESET;
long time_to_cleanup = TIME_TO_CLEAN_UP;
/* A value <= 0 disables the reset/cleanup */
long time_to_swap = TIME_TO_SWAP;
long time_to_swap_variables = TIME_TO_SWAP_VARIABLES;
/* A value <= 0 disables the swapping. */
int port_numbers[MAXNUMPORTS] = { PORTNO };
/* The login port numbers.
* Negative numbers are not ports, but the numbers of inherited
* socket file descriptors.
*/
int numports = 0; /* Number of specified ports */
int udp_port = UDP_PORT;
/* Port number for UDP. A negative number disables it. */
char *erq_file = NULL; /* Base- or pathname of the erq executable, or NULL */
char **erq_args = NULL; /* Optional arguments of the erq executable, or NULL */
char *mud_lib; /* Path to the mudlib */
char master_name[100] = ""; /* Name of the master object */
static int new_mudlib = 0; /* True: mudlib directory was specified */
static int no_erq_demon = 0; /* True: don't start the erq */
#ifndef COMPAT_MODE
Bool compat_mode = MY_FALSE; /* Plain mode */
#else
Bool compat_mode = MY_TRUE; /* Compat mode */
#endif
/* -- Other Global Variables -- */
svalue_t const0, const1;
/* The values 0 and 1 as svalues, mem-copied when needed */
double consts[5]; /* Weight constants used to compute average figures */
char *debug_file = NULL; /* Name of the debug log file. */
object_t dummy_current_object_for_loads;
/* Dummy object for functions, which need a current_object though
* there is none. This is usually the case when (re)loading the
* master object.
*/
int slow_shut_down_to_do = 0;
/* If non-zero, the game should perform a graceful shutdown.
* The value is the number of minutes to still last before shutting down.
*/
Bool reopen_debug_log = MY_FALSE;
/* Set to TRUE by the USR2 handler to force the driver to reopen
* the debug.log file.
*/
/*-------------------------------------------------------------------------*/
/* Forward declarations for the argument parser in the lower half */
static int getargs (int argc, char ** argv, int (*opt_eval)(int, const char *) );
static int eval_arg (int, const char *);
/* Datastructures used to gather data during the argument scan which
* needs to be evaluated later.
*
* struct FData: the values given to the '-f' options, allocated to size.
*/
typedef struct FData {
struct FData * next;
char txt[1]; /* The value, allocated to size */
} FData;
static FData * f_head = NULL;
static FData * f_tail = NULL;
/*-------------------------------------------------------------------------*/
int
main (int argc, char **argv)
/* The main function. Nuff said. */
{
int i;
char *p;
sigset_t set;
/* On some systems, SIGALRM is sometimes blocked.
* The reasons are unknown, but this seems to be the cure.
*/
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_UNBLOCK, &set, 0);
/* Initialisations */
setlocale(LC_CTYPE, ""); /* Use the locale defined in the LANG env var */
get_stack_direction();
init_interpret();
#ifdef RXCACHE_TABLE
rxcache_init();
#endif
put_number(&const0, 0);
put_number(&const1, 1);
assoc_shared_string_key.type = T_STRING;
assoc_shared_string_key.x.string_type = STRING_SHARED;
current_time = get_current_time();
random_seed = (uint32)current_time;
seed_random(random_seed);
dummy_current_object_for_loads = NULL_object;
#ifdef DEBUG
if (dummy_current_object_for_loads.user)
{
fprintf(stderr, "Assigning NULL_object does not clear the target.\n");
exit(1);
}
#endif
dummy_current_object_for_loads.ref = 1;
dummy_current_object_for_loads.user = &default_wizlist_entry;
#ifdef STRICT_EUIDS
strict_euids = MY_TRUE;
#endif
/*
* Check that the definition of EXTRACT_UCHAR() is correct.
*/
p = (char *)&i;
*p = -10;
if (EXTRACT_UCHAR(p) != 0x100 - 10) {
fprintf(stderr, "Bad definition of EXTRACT_UCHAR().\n");
exit(1);
}
init_rusage();
#ifdef HOST_DEPENDENT_INIT
HOST_DEPENDENT_INIT
#endif
/* Select a sensible default for the wizlist file.
* This must be done before the parsing of the arguments so
* that the name can be removed by commandline option.
*/
#ifdef WIZLIST_FILE
if ('\0' == wizlist_name[0])
{
name_wizlist_file(WIZLIST_FILE);
}
#endif
/* First scan of the arguments.
* This evaluates everything but the 'f' arguments.
*/
if (getargs(argc, argv, eval_arg))
exit(1);
/* Setup comm::host_name, so that we can open the debug.log file
* with the proper name. We do the complete setup later.
*/
initialize_host_name(hostname);
/* Change to the mudlib dir early so that the debug.log file
* is opened in the right place.
* If a mudlib dir has been given by command option, we are already
* in it.
*/
if (!new_mudlib && chdir(MUD_LIB) == -1) {
printf("%s Bad mudlib directory: %s\n", time_stamp(), MUD_LIB);
exit(1);
}
/* If the name of the debug log file hasn't been set, use a sensible
* default and make it available in the macro __DEBUG_LOG__. This should
* happen before the first debug_message().
*/
if (!debug_file)
{
char buf[MAXHOSTNAMELEN+40];
char * name;
struct lpc_predef_s *tmp;
if (compat_mode)
strcpy(buf, "__DEBUG_LOG__=\"");
else
strcpy(buf, "__DEBUG_LOG__=\"/");
name = buf + strlen(buf);
sprintf(name, "%s.debug.log", query_host_name());
debug_file = strdup(name);
strcat(name, "\"");
tmp = (struct lpc_predef_s *) xalloc(sizeof(struct lpc_predef_s));
tmp->flag = string_copy(buf);
tmp->next = lpc_predefs;
lpc_predefs = tmp;
}
printf("%s LDMud %s" LOCAL_LEVEL " (" PROJ_VERSION ")\n"
, time_stamp(), IS_RELEASE() ? GAME_VERSION : LONG_VERSION
);
debug_message("%s LDMud %s" LOCAL_LEVEL " (" PROJ_VERSION ")\n"
, time_stamp(), IS_RELEASE() ? GAME_VERSION : LONG_VERSION
);
/* This also assures the existance of the fd for the debug log */
printf("%s Random seed: 0x%lx\n"
, time_stamp(), (unsigned long)random_seed);
debug_message("%s Random seed: 0x%lx\n"
, time_stamp(), (unsigned long)random_seed);
/* If the master_name hasn't been set, select a sensible default */
if ('\0' == master_name[0])
{
#ifdef MASTER_NAME
strcpy(master_name, MASTER_NAME);
#elif defined(COMPAT_MODE)
strcpy(master_name, COMPAT_MASTER);
#else
strcpy(master_name, PLAIN_MASTER);
#endif
}
/* Make sure the name of the master object is sensible.
* This is important for modules like the lexer which
* use it directly.
*/
{
const char *pName = make_name_sane(master_name, MY_FALSE);
if (pName)
strcpy(master_name, pName);
}
if (numports < 1) /* then use the default port */
numports = 1;
init_driver_hooks();
reserve_memory();
init_shared_strings();
init_otable();
for (i = 0; i < (int)(sizeof consts / sizeof consts[0]); i++)
consts[i] = exp(- i / 900.0);
reset_machine(MY_TRUE); /* Cold reset the machine */
init_lexer(); /* Might raise an error()! */
RESET_LIMITS;
CLEAR_EVAL_COST;
{
char path[MAXPATHLEN+1];
#ifdef HAVE_GETCWD
if (!getcwd(path, sizeof(path) ))
#else
if (!getwd(path))
#endif
{
perror("get(c)wd failed");
fatal("must be able to obtain current directory name\n");
}
mud_lib = string_copy(path);
}
#ifdef ERQ_DEMON
/* Make sure that erq_file contains a complete absolute pathname. */
if (!erq_file)
{
erq_file = malloc(strlen(BINDIR)+6);
if (!erq_file)
{
fatal("Out of memory for erq pathname (%lu bytes).\n"
, (unsigned long)strlen(BINDIR)+6);
}
strcpy(erq_file, BINDIR);
#ifndef MSDOS_FS
strcat(erq_file, "/erq");
#else
strcat(erq_file, "\\erq");
#endif
}
else if (*erq_file != '/')
{
char * tmp;
tmp = malloc(strlen(BINDIR)+1+strlen(erq_file)+1);
if (!tmp)
{
fatal("Out of memory for erq pathname (%lu bytes).\n"
, (unsigned long)(strlen(BINDIR)+2+strlen(erq_file)));
}
strcpy(tmp, BINDIR);
#ifndef MSDOS_FS
strcat(tmp, "/");
#else
strcat(tmp, "\\");
#endif
strcat(tmp, erq_file);
free(erq_file);
erq_file = tmp;
}
if (!no_erq_demon)
start_erq_demon("");
#endif /* ERQ_DEMON */
initialize_host_ip_number(hostname, hostaddr);
free(hostname); hostname = NULL;
free(hostaddr); hostaddr = NULL;
(void)signal(SIGFPE, SIG_IGN);
current_object = &dummy_current_object_for_loads;
if (setjmp(toplevel_context.con.text)) {
clear_state();
add_message("Anomaly in the fabric of world space.\n");
}
else
{
toplevel_context.rt.type = ERROR_RECOVERY_BACKEND;
master_ob = get_object(master_name);
}
current_object = master_ob;
toplevel_context.rt.type = ERROR_RECOVERY_NONE;
if (master_ob == NULL) {
printf("%s The file %s must be loadable.\n"
, time_stamp(), master_name);
exit(1);
}
/* Make sure master_ob is never made a dangling pointer.
* Look at apply_master_ob() for more details.
*/
ref_object(master_ob, "main");
initialize_master_uid();
push_number(0);
callback_master(STR_INAUGURATE, 1);
setup_print_block_dispatcher();
/* Evaluate all the 'f' arguments we received, if any. */
while (f_head != NULL)
{
FData * fdata = f_head;
f_head = f_head->next;
push_volatile_string(fdata->txt);
(void)callback_master(STR_FLAG, 1);
free(fdata);
if (game_is_being_shut_down) {
fprintf(stderr, "%s Shutdown by master object.\n", time_stamp());
exit(0);
}
}
#ifdef DEBUG
if (d_flag > 1 && time_to_swap_variables <= 0)
check_a_lot_ref_counts_flag = MY_TRUE;
#endif
get_simul_efun_object();
if (game_is_being_shut_down)
exit(1);
load_wiz_file();
preload_objects(e_flag);
/* Start the backend loop. This won't return before
* the game shuts down.
*/
backend();
/* Shutdown the game.
*/
printf("%s LDMud shutting down.\n", time_stamp());
callback_master(STR_SHUTDOWN, 0);
ipc_remove();
remove_all_players();
handle_newly_destructed_objects();
/* Will perform the remove_interactive calls */
unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
remove_all_objects();
purge_action_sent();
purge_shadow_sent();
remove_wiz_list();
#if defined(MALLOC_smalloc)
dump_malloc_data();
#endif
#endif
#if defined(AMIGA) && !defined(__GNUC__)
amiga_end();
#endif
return 0; /* TODO: There are constants for this */
} /* main() */
/*-------------------------------------------------------------------------*/
void initialize_master_uid (void)
/* After loading the master object, determine its (e)uid by calling the
* lfun get_master_uid() in it. For details, better read the code.
*/
{
svalue_t *ret;
ret = apply_master(STR_GET_M_UID, 0);
if (ret && ret->type == T_NUMBER && ret->u.number)
{
master_ob->user = &default_wizlist_entry;
master_ob->eff_user = 0;
}
else if (ret == 0 || ret->type != T_STRING)
{
printf("%s %s: %s() in %s does not work\n"
, time_stamp(), strict_euids ? "Fatal" : "Warning"
, STR_GET_M_UID, master_name);
if (strict_euids)
exit(1);
}
else
{
master_ob->user = add_name(ret->u.string);
master_ob->eff_user = master_ob->user;
}
} /* initialize_master_uid() */
/*-------------------------------------------------------------------------*/
void
vdebug_message(char *fmt, va_list va)
/* Print a message into the debug logfile, vprintf() style.
*/
{
static FILE *fp = NULL;
if (fp == NULL || reopen_debug_log) {
if (fp != NULL)
{
fclose(fp);
fp = NULL;
}
reopen_debug_log = MY_FALSE;
if (!debug_file) /* We can get called before it's been set */
return;
#if defined(AMIGA) && !defined(__GNUC__)
fp = fopen(debug_file, "a");
#else
fp = fopen(debug_file, "w");
#endif
if (fp == NULL) {
perror(debug_file);
abort();
}
}
(void)vfprintf(fp, fmt, va);
(void)fflush(fp);
} /* vdebug_message() */
/*-------------------------------------------------------------------------*/
void
debug_message(char *a, ...)
/* Print a message into the debug logfile, printf() style.
*/
{
va_list va;
va_start(va, a);
vdebug_message(a, va);
va_end(va);
} /* debug_message() */
/*-------------------------------------------------------------------------*/
void
reallocate_reserved_areas (void)
/* Try to reallocate the reserved memory areas. If this is possible,
* a pending slow-shutdown is canceled and the out_of_memory flag is reset.
*/
{
char *p;
malloc_privilege = MALLOC_USER;
if (reserved_system_size && !reserved_system_area) {
if ( !(reserved_system_area = xalloc((size_t)reserved_system_size)) ) {
slow_shut_down_to_do = 1;
return;
}
else {
p = "Reallocated System reserve.\n";
write(1, p, strlen(p));
}
}
if (reserved_master_size && !reserved_master_area) {
if ( !(reserved_master_area = xalloc((size_t)reserved_master_size)) ) {
slow_shut_down_to_do = 1;
return;
}
else {
p = "Reallocated Master reserve.\n";
write(1, p, strlen(p));
}
}
if (reserved_user_size && !reserved_user_area) {
if ( !(reserved_user_area = xalloc((size_t)reserved_user_size)) ) {
slow_shut_down_to_do = 6;
return;
}
else {
p = "Reallocated User reserve.\n";
write(1, p, strlen(p));
}
}
slow_shut_down_to_do = 0;
out_of_memory = MY_FALSE;
} /* reallocate_reserved_areas() */
/*-------------------------------------------------------------------------*/
void
write_x (int d, p_uint i)
/* Memory safe function to write hexvalue <i> to fd <d>. */
{
int j;
char c;
for (j = 2 * sizeof i; --j >= 0; i <<= 4) {
c = (char)((i >> (8 * sizeof i - 4) ) + '0');
if (c >= '9' + 1)
c += (char)('a' - ('9' + 1));
write(d, &c, 1);
}
}
/*-------------------------------------------------------------------------*/
void
writed (int d, p_uint i)
/* Memory safe function to write integer value <i> to fd <d>. */
{
p_uint j;
char c;
for (j = 1000000000; j > i; j /= 10) NOOP;
if (!j) j = 1;
do {
c = (char)((i / j) % 10 + '0');
write(d, &c, 1);
j /= 10;
} while (j > 0);
}
/*-------------------------------------------------------------------------*/
void
writes (int d, const char *s)
/* Memory safe function to string <s> to fd <d>. */
{
write(d, s, strlen(s));
}
/*-------------------------------------------------------------------------*/
char *
dprintf_first (int fd, char *s, p_int a)
/* Write the string <s> up to the next "%"-style argument to <fd>, the
* write <a> according to the %-formatter. Recognized are %s, %d, and %a.
* If no %-formatter is present, the whole string is written.
*
* Result is a pointer to the remaining string.
*/
{
char *p;
do {
if ( !(p = strchr(s, '%')) )
{
write(fd, s, strlen(s));
return "";
}
write(fd, s, p - s);
switch(p[1])
{
case '%':
write(fd, p+1, 1);
continue;
case 's':
write(fd, (char *)a, strlen((char*)a));
break;
case 'd':
writed(fd, a);
break;
case 'x':
write_x(fd, a);
break;
}
return p+2;
} while(1);
} /* dprintf_first() */
/*-------------------------------------------------------------------------*/
void
dprintf1 (int fd, char *s, p_int a)
/* Write a message <s> to <fd>. <s> may contain one %-style formatter.
* for the argument <a>.
*/
{
s = dprintf_first(fd, s, a);
write(fd, s, strlen(s));
} /* dprintf1() */
/*-------------------------------------------------------------------------*/
void
dprintf2 (int fd, char *s, p_int a, p_int b)
/* Write a message <s> to <fd>. <s> may contain two %-style formatter.
* for the arguments <a> and <b>.
*/
{
s = dprintf_first(fd, s, a);
dprintf1(fd, s, b);
} /* dprintf2() */
/*-------------------------------------------------------------------------*/
void
dprintf3 (int fd, char *s, p_int a, p_int b, p_int c)
/* Write a message <s> to <fd>. <s> may contain three %-style formatter.
* for the arguments <a>, <b> and <c>.
*/
{
s = dprintf_first(fd, s, a);
dprintf2(fd, s, b, c);
} /* dprintf3() */
/*-------------------------------------------------------------------------*/
void
dprintf4 (int fd, char *s, p_int a, p_int b, p_int c, p_int d)
/* Write a message <s> to <fd>. <s> may contain three %-style formatter.
* for the arguments <a>, <b>, <c> and <d>.
*/
{
s = dprintf_first(fd, s, a);
dprintf3(fd, s, b, c, d);
} /* dprintf4() */
/*=========================================================================*/
/* The argument parser */
/*=========================================================================*/
/* This code parses the arguments passed to the program in the count <argc>
* and the array of strings <argv>. The parser distinguishes options, which
* start with a '-', from normal arguments; options are further distinguished
* by their name and may take an additional value. The parser neither
* imposes nor expects any order of options and arguments.
*
* Options are recognized in two forms. In the short form the option must
* be given as a single '-' followed by a single letter. In the long form,
* options start with '--' followed by a string of arbitrary length.
* Short options are case sensitive, long options aren't.
* Examples are: '-r' and '--recursive'.
*
* If an option takes a value, it must follow the option immediately after
* a separating space or '='. Additionally, the value for a short option
* may follow the option without separator. Examples are: '-fMakefile',
* '-f Makefile', '--file=Makefile' and '--file Makefile'.
*
* Short options may be collated into one argument, e.g. '-rtl', but
* of these only the last may take a value.
*
* The option '--' marks the end of options. All following command arguments
* are considered proper arguments even if they start with a '-' or '--'.
*
* The arguments are usually taken from the commandline; but the parser
* is also able to read them from a textfiles, which can be nested. The
* content of the textfiles is broken down into words delimited by whitespace,
* which are then treated as given on the commandline at the place where
* the instruction to read the textfile stood.
*
* The file parser recognizes simple double-quoted strings, which must be
* contained on a single line. Additionally, the '#' character given by
* itself is a comment marker - everthing after the '#' until the end
* of the current line is ignored.
*-------------------------------------------------------------------------
* Internally every option recognized by the program is associated with
* an id number, defined as the enum OptNumber. The parser itself uses the
* two id numbers 'cUnknown' for unrecognized options, and 'cArgument' for
* proper command arguments.
*
* Id numbers are associated with their option strings/letters by the
* statically initialized arrays aShortOpts and aLongOpts. Every element
* of these two arrays is a structure defining the option's name (string
* or letter), the associated id number, and whether or not the option
* takes a value. The order of the elements does not matter.
*
* The parsing is done by calling the function
*
* int getargs(int argc, char ** argv, int (*)handler(int, const char *))
*
* The function is passed the argument count <argc> and vector <argv> as
* they were received from the main() function, and a callback function
* <handler>. getargs() returns 0 if the parsing completed successfully,
* and non-zero else.
*
* The handler function is called for every successfully recognized option
* and argument. Its prototype is
*
* int handler(int eOption, const char *pValue)
*
* Parameter <eOption> denotes the recognized option, and pValue points
* to the beginning of the value string if the option takes a value.
* Proper arguments are parsed with eOption==cArgument and pValue
* pointing to the argument string. The handler has to return one of the
* following values:
* hrSuccess: if the option/argument was processed correctly.
* hrError: if the option/argument couldn't be processed.
* hrArgFile: if the given value is the name of an arguments file
* to include.
*-------------------------------------------------------------------------
*/
/* Handler return values */
typedef enum HandlerResult {
hrSuccess = 0 /* Argument parsed */
, hrError /* Error parsing argument */
, hrArgFile /* Value of this argument is the filename of an argument
* file.
*/
} HandlerResult;
/* Desription of short ('-') options */
typedef struct ShortOpt {
char cOption; /* The option character */
int eNumber; /* The associated option number */
short bValue; /* True: takes a value */
} ShortOpt;
/* Desription of long ('--') options */
typedef struct LongOpt {
char * pOption; /* The option string */
int eNumber; /* The associated option number */
short bValue; /* True: takes a value */
} LongOpt;
/* Every recognized option has a ordinal number */
typedef enum OptNumber {
cUnknown = 0 /* unknown option */
, cArgument /* normal argument (for us: portnumber) */
, cArgFile /* --args */
, cInherited /* --inherit */
, cUdpPort /* --udp */
, cTrace /* --list-compiles */
, cCleanupTime /* --cleanup-time */
, cCompat /* --compat */
, cNoCompat /* --no-compat */
, cDebug /* --debug */
, cDefine /* --define */
, cErq /* --erq */
, cEvalcost /* --eval-cost */
, cFuncall /* --funcall */
, cMaster /* --master */
, cMudlib /* --mudlib */
, cDebugFile /* --debug-file */
, cHostname /* --hostname */
, cHostaddr /* --hostaddr */
, cMaxMalloc /* --max-malloc */
, cMaxArray /* --max-array */
, cMaxBytes /* --max-bytes */
, cMaxCallouts /* --max-callouts */
, cMaxFile /* --max-file */
, cMaxMapping /* --max-mapping */
, cMaxThreadPend /* --max-thread-pending */
, cMinMalloc /* --min-malloc */
, cMinSmallMalloc /* --min-small-malloc */
, cNoERQ /* --no-erq */
, cNoHeart /* --no-heart */
, cNoPreload /* --no-preload */
, cPidFile /* --pidfile */
, cRandomSeed /* --random-seed */
, cResetTime /* --reset-time */
, cReserved /* -r */
, cReserveUser /* --reserve-user */
, cReserveMaster /* --reserve-master */
, cReserveSystem /* --reserve-system */
, cStrictEuids /* --strict-euids */
, cNoStrictEuids /* --no-strict-euids */
, cSwap /* -s */
, cSwapTime /* --swap-time */
, cSwapVars /* --swap-variables */
, cSwapFile /* --swap-file */
, cSwapCompact /* --swap-compact */
, cWizlistFile /* --wizlist-file */
, cNoWizlistFile /* --no-wizlist-file */
#ifdef GC_SUPPORT
, cGcollectFD /* --gcollect-outfd */
#endif
#ifdef DEBUG
, cCheckRefs /* --check-refcounts */
, cCheckState /* --check-state */
, cGobbleFDs /* --gobble-descriptors */
#endif
#ifdef CHECK_STRINGS
, cCheckStrings /* --check-strings */
#endif
#ifdef YYDEBUG
, cYYDebug /* --yydebug */
#endif
, cOptions /* --options */
, cVersion /* --version */
, cLongHelp /* --longhelp */
, cHelp /* --help */
} OptNumber;
/* Comprehensive lists of recognized options */
static ShortOpt aShortOpts[]
= { { 'c', cTrace, MY_FALSE }
, { 'D', cDefine, MY_TRUE }
, { 'd', cDebug, MY_FALSE }
, { 'E', cEvalcost, MY_TRUE }
, { 'e', cNoPreload, MY_FALSE }
, { 'f', cFuncall, MY_TRUE }
, { 'M', cMaster, MY_TRUE }
, { 'm', cMudlib, MY_TRUE }
, { 'N', cNoERQ, MY_FALSE }
, { 'P', cInherited, MY_TRUE }
, { 'r', cReserved, MY_TRUE }
, { 's', cSwap, MY_TRUE }
, { 't', cNoHeart, MY_FALSE }
, { 'u', cUdpPort, MY_TRUE }
#ifdef YYDEBUG
, { 'y', cYYDebug, MY_FALSE }
#endif
, { 'V', cVersion, MY_FALSE }
, { 'h', cHelp, MY_FALSE }
, { '?', cHelp, MY_FALSE }
};
static LongOpt aLongOpts[]
= { { "args", cArgFile, MY_TRUE }
, { "cleanup-time", cCleanupTime, MY_TRUE }
, { "compat", cCompat, MY_FALSE }
, { "no-compat", cNoCompat, MY_FALSE }
, { "debug", cDebug, MY_FALSE }
, { "define", cDefine, MY_TRUE }
, { "debug-file", cDebugFile, MY_TRUE }
, { "debug_file", cDebugFile, MY_TRUE } /* TODO: COMPAT */
, { "erq", cErq, MY_TRUE }
, { "eval-cost", cEvalcost, MY_TRUE }
, { "funcall", cFuncall, MY_TRUE }
, { "list-compiles", cTrace, MY_FALSE }
, { "hostname", cHostname, MY_TRUE }
, { "hostaddr", cHostaddr, MY_TRUE }
, { "master", cMaster, MY_TRUE }
, { "mudlib", cMudlib, MY_TRUE }
, { "max-malloc", cMaxMalloc, MY_TRUE }
, { "max_malloced", cMaxMalloc, MY_TRUE } /* TODO: COMPAT */
, { "max-array", cMaxArray, MY_TRUE }
, { "max-bytes", cMaxBytes, MY_TRUE }
, { "max-callouts", cMaxCallouts, MY_TRUE }
, { "max-file", cMaxFile, MY_TRUE }
, { "max-mapping", cMaxMapping, MY_TRUE }
, { "max-thread-pending", cMaxThreadPend, MY_TRUE }
, { "min-malloc", cMinMalloc, MY_TRUE }
, { "min-small-malloc", cMinSmallMalloc, MY_TRUE }
, { "inherit", cInherited, MY_TRUE }
, { "no-erq", cNoERQ, MY_FALSE }
, { "no-heart", cNoHeart, MY_FALSE }
, { "no-preload", cNoPreload, MY_FALSE }
, { "pidfile", cPidFile, MY_TRUE }
, { "random-seed", cRandomSeed, MY_TRUE }
, { "reset-time", cResetTime, MY_TRUE }
, { "reserve-user", cReserveUser, MY_TRUE }
, { "reserve-master", cReserveMaster, MY_TRUE }
, { "reserve-system", cReserveSystem, MY_TRUE }
, { "strict-euids", cStrictEuids, MY_FALSE }
, { "no-strict-euids", cNoStrictEuids, MY_FALSE }
, { "swap-time", cSwapTime, MY_TRUE }
, { "swap-variables", cSwapVars, MY_TRUE }
, { "swap-file", cSwapFile, MY_TRUE }
, { "swap-compact", cSwapCompact, MY_FALSE }
, { "wizlist-file", cWizlistFile, MY_TRUE }
, { "no-wizlist-file", cNoWizlistFile, MY_FALSE }
#ifdef GC_SUPPORT
, { "gcollect-outfd", cGcollectFD, MY_TRUE }
, { "gcollect_outfd", cGcollectFD, MY_TRUE } /* TODO: COMPAT */
#endif
, { "udp", cUdpPort, MY_TRUE }
#ifdef DEBUG
, { "check-refcounts", cCheckRefs, MY_FALSE }
, { "check-state", cCheckState, MY_TRUE }
, { "gobble-descriptors", cGobbleFDs, MY_TRUE }
, { "check_a_lot_of_ref_counts", cCheckRefs, MY_FALSE } /* TODO: COMPAT */
, { "gobble_descriptors", cGobbleFDs, MY_TRUE } /* TODO: COMPAT */
#endif
#ifdef CHECK_STRINGS
, { "check-strings", cCheckStrings, MY_FALSE }
#endif
#ifdef YYDEBUG
, { "yydebug", cYYDebug, MY_FALSE }
#endif
, { "options", cOptions, MY_FALSE }
, { "version", cVersion, MY_FALSE }
, { "longhelp", cLongHelp, MY_FALSE }
, { "help", cHelp, MY_FALSE }
};
/*-------------------------------------------------------------------------*/
/* Internal management structure to handle an (argc,argv) input source.
* It is allocated to the proper length.
*/
typedef struct InputSource {
struct InputSource * next; /* Link pointer */
int arg; /* Number of next argument to evaluate */
int argc; /* Total number of arguments */
char ** argv; /* Allocated array of argument pointers */
char * name; /* Filename from where the arguments have been read,
* NULL for commandline */
char data[1];
/* Block holding the filename and the text read from
* the file.
*
* The data read from the file has been broken up into separate
* strings which are pointed to from the argv array.
*/
} InputSource;
/*-------------------------------------------------------------------------*/
static void
version (void)
/* Print the version of the gamedriver.
*/
{
fputs("LDMud ", stdout);
if (IS_RELEASE())
fputs(GAME_VERSION, stdout);
else
fputs(LONG_VERSION, stdout);
fputs(LOCAL_LEVEL " - a LPMud Game Driver.\n"
"\nRelease: " PROJ_VERSION "; " RELEASE_DATE
"\nCompiled: " __DATE__
#ifdef __TIME__
" " __TIME__
#endif
"\n"
, stdout);
} /* version() */
/*-------------------------------------------------------------------------*/
static void
options (void)
/* Print the version of the gamedriver and the compile time options.
*/
{
version();
fputs("\n Mode: "
#ifdef COMPAT_MODE
"Compat"
#else
"Plain (aka cross-compat)"
#endif
#ifdef STRICT_EUIDS
" with strict euids.\n"
#else
"\n"
#endif
, stdout);
fputs(" Mudlib path: " MUD_LIB "\n"
" Binary path: " BINDIR "\n"
#ifdef MASTER_NAME
" Master object: <mudlib>/" MASTER_NAME "\n"
#elif defined(COMPAT_MODE)
" Master object: <mudlib>/" COMPAT_MASTER "\n"
#else
" Master object: <mudlib>/" PLAIN_MASTER "\n"
#endif
, stdout);
printf(" Multiple ports: %d ports max, default is %d.\n", MAXNUMPORTS, PORTNO);
printf(" UDP: default port is %d.\n", UDP_PORT);
#ifdef ERQ_DEMON
printf(" ERQ: max data length: send %d / recv %d bytes.\n"
" directory: %s.\n"
, ERQ_MAX_SEND, ERQ_MAX_REPLY, ERQ_DIR);
#else
fputs(" ERQ: disabled.\n", stdout);
#endif
#ifdef USE_PTHREADS
fputs(" PThreads: supported.\n", stdout);
#else
fputs(" PThreads: not supported.\n", stdout);
#endif
#ifndef USE_IPV6
fputs(" IPv6: not supported.\n", stdout);
#else
fputs(" IPv6: supported.\n", stdout);
#endif
#ifndef USE_MYSQL
fputs(" mySQL: not supported.\n", stdout);
#else
fputs(" mySQL: supported.\n", stdout);
#endif
#ifndef USE_PCRE
fputs(" PCRE: not supported.\n", stdout);
#else
fputs(" PCRE: supported.\n", stdout);
#endif
#ifdef ACCESS_CONTROL
fputs(" Access control: using <mudlib>/" ACCESS_FILE
# ifdef ACCESS_LOG
", logs into <mudlib>/" ACCESS_LOG "\n"
# else
", no logs.\n"
# endif
, stdout);
#else
fputs(" Access control: disabled.\n", stdout);
#endif
#ifdef WIZLIST_FILE
fputs(" Wizlist: saved in <mudlib>/" WIZLIST_FILE "\n", stdout);
#else
fputs(" Wizlist: not saved\n", stdout);
#endif
/* Print the language options, nicely indented. */
{
char * optstrings[] = { "" /* have at least one string in here */
#ifdef SUPPLY_PARSE_COMMAND
, "parse_command() enabled\n"
#endif
#ifdef USE_PROCESS_STRING
, "process_string() enabled\n"
#endif
#ifdef USE_SET_LIGHT
, "set_light() enabled\n"
#endif
#ifdef USE_SET_IS_WIZARD
, "set_is_wizard() enabled\n"
#endif
#ifdef INITIALIZATION_BY___INIT
, "initialization by __INIT()\n"
#else
, "static initialization\n"
#endif
#ifdef USE_LPC_NOSAVE
, "'nosave' enabled\n"
#endif
#ifdef USE_DEPRECATED
, "obsolete and deprecated efuns enabled\n"
#endif
#ifdef NO_NEGATIVE_RANGES
, "assignments to negative ranges disabled\n"
#endif
};
size_t nStrings = sizeof(optstrings) / sizeof(optstrings[0]);
size_t i;
for (i = 1; i < nStrings; i++)
{
if (1 == i)
fputs(" Language: ", stdout);
else
fputs(" ", stdout);
fputs(optstrings[i], stdout);
}
} /* print language options */
printf(" Runtime limits: max read file size: %7d\n"
" max byte read/write: %7d\n"
" max socket buf size: %7d\n"
#if defined(USE_PTHREADS)
" max pthread write size: %7d\n"
#endif /* USE_PTHREADS */
" max eval cost: %9d %s\n"
" catch eval cost: %7d\n"
" master eval cost: %7d\n"
" eval stack: %7d\n"
" user call depth: %7d\n"
" max call depth: %7d\n"
" max bitfield length: %7d\n"
" max array size: %7d\n"
" max mapping size: %7d\n"
" max number callouts: %7d\n"
" max number players: %7d\n"
" ed cmd/cmd ratio: %7d:1\n"
#if defined(TRACE_CODE)
" max trace length: %7d\n"
#endif
, READ_FILE_MAX_SIZE, MAX_BYTE_TRANSFER
, SET_BUFFER_SIZE_MAX
#if defined(USE_PTHREADS)
, PTHREAD_WRITE_MAX_SIZE
#endif /* USE_PTHREADS */
, MAX_COST
#if defined(DYNAMIC_COSTS)
, "(dynamic)"
#else
, ""
#endif
, CATCH_RESERVED_COST, MASTER_RESERVED_COST
, EVALUATOR_STACK_SIZE
, MAX_USER_TRACE, MAX_TRACE
, MAX_BITS, MAX_ARRAY_SIZE, MAX_MAPPING_SIZE
, MAX_CALLOUTS, MAX_PLAYERS
, ALLOWED_ED_CMDS /* MAX_CMDS_PER_BEAT is not implemented */
#ifdef TRACE_CODE
, TOTAL_TRACE_LENGTH
#endif
);
printf(" Timing: reset: %7d s\n"
" clean up: %7d s\n"
, TIME_TO_RESET, TIME_TO_CLEAN_UP
);
printf(" Swapping: objects ");
if (TIME_TO_SWAP > 0)
printf("after %4d s\n", TIME_TO_SWAP);
else
printf(" never\n");
printf(" variables ");
if (TIME_TO_SWAP_VARIABLES > 0)
printf("after %4d s\n", TIME_TO_SWAP_VARIABLES);
else
printf(" never\n");
if (SWAP_FILE[0] == '/')
printf(" file: %s.<host>\n"
, SWAP_FILE
);
else
printf(" file: <mudlib>/%s.<host>\n"
, SWAP_FILE
);
printf(" Compiler: max stack size: %6d\n"
" max local variables: %6d\n"
" max define length: %6d\n"
#ifdef ALIGN_FUNCTIONS
" functions are aligned.\n"
#endif
, COMPILER_STACK_SIZE
, MAX_LOCAL
, DEFMAX
);
printf(" Memory: using %s\n"
" reserved user size: %8d\n"
" reserved master size: %8d\n"
" reserved system size: %8d\n"
" initial allocation: %9d\n"
" initial small alloc: %8d\n"
#ifdef MALLOC_sysmalloc
, "system malloc"
#elif defined(MALLOC_smalloc)
, "smalloc"
# if defined(MALLOC_TRACE)
# if defined(MALLOC_LPC_TRACE)
" (trace enabled, lpc-trace enabled)"
# else
" (trace enabled)"
# endif
# elif defined(MALLOC_LPC_TRACE)
" (lpc-trace enabled)"
# endif
#else
, "unknown malloc"
#endif
, RESERVED_USER_SIZE
, RESERVED_MASTER_SIZE
, RESERVED_SYSTEM_SIZE
, MIN_MALLOCED
, MIN_SMALL_MALLOCED
);
printf(" max allocation: ");
if (MAX_MALLOCED > 0)
printf("%9d\n", MAX_MALLOCED);
else
printf("unlimited\n");
printf("Internal tables: shared string hash: %6d entries\n"
" object hash: %6d entries\n"
" reserved name hash: %6d entries\n"
" apply cache: %6d entries\n"
#ifdef RXCACHE_TABLE
" regexp cache: %6d entries\n"
#endif
, HTABLE_SIZE
, OTABLE_SIZE
, ITABLE_SIZE
, 1<<APPLY_CACHE_BITS
#ifdef RXCACHE_TABLE
, RXCACHE_TABLE
#endif
);
#ifdef DEBUG
printf(" Debug options: check state: %d ("
, check_state_level
);
switch (check_state_level)
{
case 0: fputs("never", stdout); break;
case 1: fputs("once per loop", stdout); break;
case 2: fputs("several times per loop", stdout); break;
default: fputs("???", stdout); break;
}
fputs(")\n", stdout);
if (check_a_lot_ref_counts_flag)
fputs(" check refcounts\n", stdout);
else
fputs(" don't check refcounts\n", stdout);
#endif
/* Print the other options, nicely formatted. */
{
char * optstrings[] = { " Other options: "
# if defined(DEBUG)
, "DEBUG"
# endif
# if defined(CHECK_STRINGS)
, "CHECK_STRINGS"
# endif
# if defined(KEEP_STRINGS)
, "KEEP_STRINGS"
# endif
# if defined(DEBUG_TELNET)
, "DEBUG_TELNET"
# endif
# if defined(DEBUG_SMALLOC_ALLOCS)
, "DEBUG_SMALLOC_ALLOCS"
# endif
# if defined(YYDEBUG)
, "YYDEBUG"
# endif
# if defined(NO_INLINES)
, "NO_INLINES"
# endif
# if defined(MSDOS_FS)
, "MSDOS_FS"
# endif
# if defined(TRACECODE)
, "TRACECODE"
# endif
# if defined(COMM_STAT)
, "COMM_STAT"
# endif
# if defined(APPLY_CACHE_STAT)
, "APPLY_CACHE_STAT"
# endif
# if defined(OPCPROF)
, "OPCPROF"
# if defined(OPCPROF_VERBOSE)
, "OPCPROF_VERBOSE"
# endif
# endif
# if defined(CHECK_OBJECT_GC_REF)
, "CHECK_OBJECT_GC_REF"
# endif
# if defined(CHECK_SMALLOC_TOTAL)
, "CHECK_SMALLOC_TOTAL"
# endif
# if defined(DUMP_GC_REFS)
, "DUMP_GC_REFS"
# endif
};
size_t nStrings = sizeof(optstrings) / sizeof(optstrings[0]);
size_t iInitial = strlen(optstrings[0]);
size_t curlen = 0;
size_t i;
if (nStrings > 1)
{
fputs(optstrings[0], stdout);
curlen = iInitial;
for (i = 1; i < nStrings; i++)
{
curlen += strlen(optstrings[i]) + 2;
if (curlen > 78)
{
printf("\n%*s", (int)iInitial, " ");
curlen = iInitial;
}
fputs(optstrings[i], stdout);
if (i < nStrings-1)
fputs(", ", stdout);
}
fputs(".\n", stdout);
}
}
} /* options() */
/*-------------------------------------------------------------------------*/
static void
shortusage (void)
/* Print the short help information to stdout. */
{
version();
fputs("\n"
"Usage: driver [options] [<portnumber>...]\n"
"\nOptions are:\n"
"\n"
" --args <filename\n"
" -P|--inherit <fd-number>\n"
" -u|--udp <portnumber>\n"
" -D|--define <macro>[=<text>]\n"
" -E|--eval-cost <ticks>\n"
" -M|--master <filename>\n"
" -m|--mudlib <pathname>\n"
" --debug-file <filename>\n"
" --hostname <hostname>\n"
" --hostaddr <hostaddr>\n"
" --compat\n"
" --no-compat\n"
" -d|--debug\n"
" -c|--list-compiles\n"
" -e|--no-preload\n"
" --erq <filename> | --erq \"<filename> <erq args>\"\n"
" -N|--no-erq\n"
" -t|--no-heart\n"
" -f|--funcall <word>\n"
" --strict-euids\n"
" --no-strict-euids\n"
" --cleanup-time\n"
" --reset-time\n"
" --max-array\n"
" --max-callouts\n"
" --max-mapping\n"
" --max-bytes\n"
" --max-file\n"
" --max-thread-pending\n"
" -s <time> | --swap-time <time>\n"
" -s v<time> | --swap-variables <time>\n"
" -s f<name> | --swap-file <name>\n"
" -s c | --swap-compact\n"
" --max-malloc <size>\n"
" --min-malloc <size>\n"
" --min-small-malloc <size>\n"
" -r u<size> | --reserve-user <size>\n"
" -r m<size> | --reserve-master <size>\n"
" -r s<size> | --reserve-system <size>\n"
" --pidfile <filename>\n"
" --wizlist-file <filename>\n"
" --no-wizlist-file\n"
#ifdef GC_SUPPORT
" --gcollect-outfd <filename>|<num>\n"
#endif
#ifdef YYDEBUG
" --y|--yydebug\n"
#endif
" --random-seed <num>\n"
#ifdef DEBUG
" --check-refcounts\n"
" --check-state <lvl>\n"
" --gobble-descriptors <num>\n"
#endif
#ifdef CHECK_STRINGS
" --check-strings\n"
#endif
" -V|--version\n"
" --options\n"
" --longhelp\n"
" -h|-?|--help\n"
, stdout);
} /* shortusage() */
/*-------------------------------------------------------------------------*/
static void
usage (void)
/* Print the help information to stdout. */
{
version();
fputs("\n"
"Usage: driver [options] [<portnumber>...]\n"
"\nOptions are:\n"
"\n"
" --args <filename>\n"
" Read the options from <filename> as if they were given on the\n"
" commandline.\n"
"\n"
" -P|--inherit <fd-number>\n"
" Inherit filedescriptor <fd-number> from the parent process\n"
" as socket to listen for connections.\n"
"\n"
" -u|--udp <portnumber>\n"
" Specify the <portnumber> for the UDP port, overriding the compiled-in\n"
" default.\n"
"\n"
" -D|--define <macro>[=<text>]\n"
" Add <macro> (optionally to be expanded to <text>) to the list of\n"
" predefined macros known by the LPC compiler.\n"
"\n"
" -E|--eval-cost <ticks>\n"
" Set the number of <ticks> available for one evaluation thread.\n"
"\n"
" -M|--master <filename>\n"
" Use <filename> for the master object.\n"
"\n"
" -m|--mudlib <pathname>\n"
" Use <pathname> as the top directory of the mudlib.\n"
"\n"
" --debug-file <filename>\n"
" Log all debug output in <filename> instead of <host>.debug.log .\n"
"\n"
" --hostname <name>\n"
" Use <name> as hostname instead of what the system says.\n"
"\n"
" --hostaddr <addr>\n"
" Use <addr> as address of this machine, instead of what the system says.\n"
" In particular this address will be used to open the driver ports.\n"
"\n"
" --compat\n"
" --no-compat\n"
" Select the mode (compat or plain) of the driver.\n"
" Note that this choice does not affect the default name of the master\n"
" object.\n"
"\n"
" -d|--debug\n"
" Generate debug output; repeat the argument for even more output.\n"
"\n"
" -c|--list-compiles\n"
" List the name of every compiled file on stderr.\n"
"\n"
" -e|--no-preload\n"
" Pass a non-zero argument (the number of occurences of this option)\n"
" to master->preload(), which usually inhibits all preloads of castles\n"
" and other objects.\n"
"\n"
" --erq <filename>\n"
" --erq \"<filename> <erq arguments>\"\n"
" Use <filename> instead of 'erq' as the name of the ERQ executable.\n"
" If the name starts with a '/', it is take to be an absolute pathname,\n"
" otherwise it is interpreted relative to " BINDIR ".\n"
" If not specified, 'erq' is used as executable name.\n"
" With the proper use of quotes it is legal to pass arbitrary arguments\n"
" to the erq, however, these may not contain spaces themselves.\n"
"\n"
" -N|--no-erq\n"
" Don't start the erq demon (if it would be started at all).\n"
"\n"
" -t|--no-heart\n"
" Disable heartbeats and call_outs.\n"
"\n"
" -f|--funcall <word>\n"
" The lfun master->flag() is called with <word> as argument before the\n"
" gamedriver accepts network connections.\n"
"\n"
" --cleanup-time <time>\n"
" The idle time in seconds for an object before the driver tries to\n"
" clean it up. This time should be substantially higher than the\n"
" reset time. A time <= 0 disables the cleanup mechanism.\n"
"\n"
" --reset-time <time>\n"
" The time in seconds for an object before it is reset.\n"
" A time <= 0 disables the reset mechanism.\n"
"\n"
" --max-array <size>\n"
" The maximum number of elements an array can hold.\n"
" Set to 0, arrays of any size are allowed.\n"
"\n"
" --max-callouts <size>\n"
" The maximum number of callouts at one time.\n"
" Set to 0, any number of callouts is allowed.\n"
"\n"
" --max-mapping <size>\n"
" The maximum number of elements a mapping can hold.\n"
" Set to 0, mappings of any size are allowed.\n"
"\n"
" --max-bytes <size>\n"
" The maximum number of bytes one read_bytes()/write_bytes() call\n"
" can handle.\n"
" Set to 0, arrays of any size are allowed.\n"
"\n"
" --max-file <size>\n"
" The maximum number of bytes one read_file()/write_file() call\n"
" can handle.\n"
" Set to 0, arrays of any size are allowed.\n"
"\n"
" --max-thread-pending <size>\n"
" The maximum number of bytes to be kept pending by the socket write\n"
" thread.\n"
" Set to 0, an unlimited amount of data can be kept pending.\n"
#ifndef USE_PTHREADS
" (Ignored since pthreads are not supported)\n"
#endif
"\n"
" -s <time> | --swap-time <time>\n"
" -s v<time> | --swap-variables <time>\n"
" Time in seconds before an object (or its variables) are swapped out.\n"
" A time less or equal 0 disables swapping.\n"
"\n"
" -s f<name> | --swap-file <name>\n"
" Swap into file <name> instead of LP_SWAP.<host> .\n"
"\n"
" -s c | --swap-compact\n"
" Reuse free space in the swap file immediately.\n"
"\n"
" --max-malloc <size>\n"
" Restrict total memory allocations to <size> bytes. A <size> of 0\n"
" or 'unlimited' removes any restriction.\n"
"\n"
" --min-malloc <size>\n"
" --min-small-malloc <size>\n"
" Determine the sizes for the explicite initial large resp. small chunk\n"
" allocation. A size of 0 disables the explicite initial allocations.\n"
"\n"
" -r u<size> | --reserve-user <size>\n"
" -r m<size> | --reserve-master <size>\n"
" -r s<size> | --reserve-system <size>\n"
" Reserve <size> amount of memory for user/master/system allocations to\n"
" be held until main memory runs out.\n"
"\n"
" --strict-euids\n"
" --no-strict-euids\n"
" Enforce/don't enforce the proper use of euids.\n"
"\n"
" --wizlist-file <filename>\n"
" --no-wizlist-file\n"
" Read and save the wizlist in the named file (always interpreted\n"
" relative the mudlib); resp. don't read or save the wizlist.\n"
"\n"
" --pidfile <filename>\n"
" Write the pid of the driver process into <filename>.\n"
"\n"
#ifdef GC_SUPPORT
" --gcollect-outfd <filename>|<num>\n"
" Garbage collector output (like a log of all reclaimed memory blocks)\n"
" is sent to <filename> (or inherited fd <num>) instead of stderr.\n"
"\n"
#endif
#ifdef YYDEBUG
" --y|--yydebug\n"
" Enable debugging of the LPC compiler.\n"
"\n"
#endif
" --random-seed <num>\n"
" Seed value for the random number generator. If not given, the\n"
" driver chooses a seed value on its own.\n"
"\n"
#ifdef DEBUG
" --check-refcounts\n"
" Every backend cycle, all refcounts in the system are checked.\n"
" SLOW!\n"
"\n"
" --check-state <lvl>\n"
" Perform a regular simplistic check of the virtual machine according\n"
" to <lvl>:\n"
" = 0: no check\n"
" = 1: once per backend loop\n"
" = 2: at various points in the backend loop\n"
"\n"
" --gobble-descriptors <num>\n"
" <num> (more) filedescriptors are used up. You'll know when you need it.\n"
"\n"
#endif
#ifdef CHECK_STRINGS
" --check-strings\n"
" Every backend cycle, all shared strings in the system are checked.\n"
" SLOW!\n"
"\n"
#endif
" -V|--version\n"
" Print the version of the driver, then exit.\n"
"\n"
" --options\n"
" Print the version and compilation options of the driver, then exit.\n"
"\n"
" --longhelp\n"
" Display this help and exit.\n"
" -h|-?|--help\n"
" Display the short help text and exit.\n"
, stdout);
} /* usage() */
/*-------------------------------------------------------------------------*/
static int
eval_arg (int eOption, const char * pValue)
/* Callback from getargs() for the first scan of the commandline
* arguments. <eOption> is the option recognized, <pValue> a value
* or NULL.
* Return hrSuccess on success, hrError on a failure.
* Return hrArgFile if the given value is the name of an arguments file.
*/
{
switch (eOption)
{
case cArgument:
if (numports >= MAXNUMPORTS)
fprintf(stderr, "Portnumber '%s' ignored.\n", pValue);
else if (atoi(pValue))
port_numbers[numports++] = atoi(pValue);
else
fprintf(stderr, "Illegal portnumber '%s' ignored.\n", pValue);
break;
case cArgFile:
return hrArgFile;
case cInherited:
if (numports >= MAXNUMPORTS)
fprintf(stderr, "fd '%s' ignored.\n", pValue);
else if (atoi(pValue))
port_numbers[numports++] = -atoi(pValue);
else
fprintf(stderr, "Illegal fd '%s' ignored.\n", pValue);
break;
case cUdpPort:
if (atoi(pValue))
udp_port = atoi(pValue);
else
fprintf(stderr, "Illegal portnumber '%s' ignored.\n", pValue);
break;
case cDefine:
{
struct lpc_predef_s *tmp;
tmp = (struct lpc_predef_s *) xalloc(sizeof(struct lpc_predef_s));
tmp->flag = string_copy(pValue);
tmp->next = lpc_predefs;
lpc_predefs = tmp;
}
break;
case cEvalcost:
{
long val;
val = atoi(pValue);
if (val >= 0)
def_eval_cost = val;
else
fprintf(stderr, "Illegal eval-cost '%s' ignored.\n", pValue);
break;
}
case cCompat:
compat_mode = MY_TRUE;
break;
case cNoCompat:
compat_mode = MY_FALSE;
break;
case cNoPreload:
e_flag++;
break;
case cErq:
{
char * begin_arg;
if (erq_file != NULL)
free(erq_file);
if (erq_args != NULL)
{
free(erq_args);
erq_args = NULL;
}
erq_file = strdup(pValue);
begin_arg = strchr(erq_file, ' ');
if (begin_arg)
{
/* Split the string into command and arguments */
int num_args;
char * cp;
/* Skip leading spaces */
*begin_arg++ = '\0';
while (*begin_arg == ' ') begin_arg++;
/* Count the arguments */
for (num_args = 0, cp = begin_arg; *cp != '\0'; )
{
/* Found an argument: skip it */
num_args++;
while (*cp != ' ' && *cp != '\0') cp++;
/* Skip trailing spaces */
while (*cp == ' ') cp++;
}
if (num_args != 0)
{
/* There are arguments!
* Put them into the argument array.
*/
erq_args = malloc(sizeof(*erq_args) * (num_args+3));
erq_args[0] = "erq";
erq_args[1] = "--forked";
erq_args[num_args+2] = NULL;
for (num_args = 2, cp = begin_arg; *cp != '\0'; )
{
/* Found an argument: store and skip it */
erq_args[num_args++] = cp;
while (*cp != ' ' && *cp != '\0') cp++;
/* Ensure termination */
*cp++ = '\0';
/* Skip trailing spaces */
while (*cp == ' ') cp++;
}
}
}
break;
}
case cNoERQ:
no_erq_demon++;
break;
case cDebug:
d_flag++;
break;
case cTrace:
comp_flag = MY_TRUE;
break;
case cNoHeart:
t_flag = MY_TRUE;
break;
case cCleanupTime:
if (atoi(pValue))
{
time_to_cleanup = atoi(pValue);
if (time_to_cleanup < 0)
time_to_cleanup = 0;
}
else
fprintf(stderr, "Illegal cleanup-time '%s' ignored.\n", pValue);
break;
case cResetTime:
if (atoi(pValue))
{
time_to_reset = atoi(pValue);
if (time_to_reset < 0)
time_to_reset = 0;
}
else
fprintf(stderr, "Illegal cleanup-time '%s' ignored.\n", pValue);
break;
case cMaxArray:
case cMaxBytes:
case cMaxCallouts:
case cMaxFile:
case cMaxMapping:
{
long val = atoi(pValue);
if (val >= 0)
{
switch(eOption)
{
case cMaxArray: def_array_size = (size_t)val; break;
case cMaxBytes: def_byte_xfer = val; break;
case cMaxCallouts: def_callouts = val; break;
case cMaxFile: def_file_xfer = val; break;
case cMaxMapping: def_mapping_size = (size_t)val; break;
}
}
else
fprintf(stderr, "Illegal value for limit '%s' ignored.\n", pValue);
break;
}
case cMaxThreadPend:
{
long val = atoi(pValue);
if (val >= 0)
pthread_write_max_size = val;
else
fprintf(stderr, "Illegal value for limit '%s' ignored.\n", pValue);
break;
}
case cSwap:
/* Compatibility vs. one-char-only options *sigh* */
switch (*pValue) {
case 'c': eOption = cSwapCompact; break;
case 'v': eOption = cSwapVars; pValue++; break;
case 'f': eOption = cSwapFile; pValue++; break;
default: eOption = cSwapTime; break;
}
/* FALLTHROUGH */
case cSwapVars:
case cSwapFile:
case cSwapTime:
case cSwapCompact:
if (cSwapTime == eOption)
{
time_to_swap = atoi(pValue);
if (time_to_swap < 0)
time_to_swap = 0;
}
else if (cSwapVars == eOption)
{
time_to_swap_variables = atoi(pValue);
if (time_to_swap_variables < 0)
time_to_swap_variables = 0;
}
else if (cSwapFile == eOption)
name_swap_file(pValue);
else /* cSwapCompact */
swap_compact_mode = MY_TRUE;
break;
case cWizlistFile:
case cNoWizlistFile:
if (cWizlistFile == eOption)
name_wizlist_file(pValue);
else /* cNoWizlistFile */
name_wizlist_file("");
break;
#ifdef YYDEBUG
case cYYDebug:
{
extern int yydebug;
yydebug = MY_TRUE;
break;
}
#endif
case cHostname:
if (hostname != NULL)
free(hostname);
hostname = strdup(pValue);
break;
case cHostaddr:
if (hostaddr != NULL)
free(hostaddr);
hostaddr = strdup(pValue);
break;
case cMaster:
if (strlen(pValue) >= sizeof(master_name)) {
fprintf(stderr, "Too long master name '%s'\n", pValue);
return hrError;
}
strcpy(master_name, pValue);
break;
case cMinMalloc:
min_malloced = strtol(pValue, (char **)0, 0);
if (min_malloced < 0)
{
fprintf(stderr, "Illegal value '%s' for --min-malloc\n", pValue);
return hrError;
}
break;
case cMinSmallMalloc:
min_small_malloced = strtol(pValue, (char **)0, 0);
if (min_small_malloced < 0)
{
fprintf(stderr, "Illegal value '%s' for --min-small-malloc\n", pValue);
return hrError;
}
break;
case cMaxMalloc:
if (!strcasecmp(pValue, "unlimited"))
{
max_malloced = 0;
}
else
{
max_malloced = strtol(pValue, (char **)0, 0);
if (max_malloced < 0)
{
fprintf(stderr, "Illegal value '%s' for --max-malloc\n", pValue);
return hrError;
}
}
break;
case cMudlib:
if (chdir(pValue) == -1) {
fprintf(stderr, "Bad mudlib directory: %s\n", pValue);
return hrError;
}
new_mudlib = 1;
break;
case cDebugFile:
if (debug_file != NULL)
free(debug_file);
debug_file = strdup(pValue);
break;
case cRandomSeed:
random_seed = strtoul(pValue, (char **)0, 0);
seed_random(random_seed);
break;
case cReserved:
case cReserveUser:
case cReserveMaster:
case cReserveSystem:
{
mp_int *sizep = &reserved_user_size;
if (cReserved == eOption)
{
/* This is a rather nasty compromise between being compatible
* to original Amylaar and the one-char-only short options.
*/
switch(*pValue++)
{
default: pValue--; /* FALLTHROUGH */
case 'u': sizep = &reserved_user_size; break;
case 'm': sizep = &reserved_master_size; break;
case 's': sizep = &reserved_system_size; break;
}
}
else
switch (eOption)
{
case cReserveUser: sizep = &reserved_user_size; break;
case cReserveMaster: sizep = &reserved_master_size; break;
case cReserveSystem: sizep = &reserved_system_size; break;
}
*sizep = strtol(pValue, (char**)0, 0);
break;
}
case cStrictEuids:
strict_euids = MY_TRUE;
break;
case cNoStrictEuids:
strict_euids = MY_FALSE;
break;
#ifdef GC_SUPPORT
case cGcollectFD:
if (isdigit((unsigned char)*pValue)) {
default_gcollect_outfd = strtol(pValue, (char **)0, 0);
} else {
default_gcollect_outfd = ixopen3(pValue, O_CREAT|O_TRUNC|O_WRONLY, 0640);
}
gcollect_outfd = default_gcollect_outfd;
break;
#endif
case cOptions:
options();
exit(0);
break;
case cVersion:
version();
exit(0);
break;
case cHelp:
shortusage();
return hrError;
case cLongHelp:
usage();
return hrError;
case cPidFile:
{
FILE * pidfile;
pidfile = fopen(pValue, "w");
if (!pidfile)
{
fprintf(stderr, "Can't open pidfile '%s': %s.\n"
, pValue, strerror(errno));
return hrError;
}
fprintf(pidfile, "%ld\n", (long)getpid());
fclose(pidfile);
break;
}
#ifdef DEBUG
case cCheckRefs:
check_a_lot_ref_counts_flag = MY_TRUE;
break;
case cCheckState:
{
int n;
char * end;
n = strtol(pValue, &end, 0);
if (n < 0 || n > 2 || end == NULL || *end != '\0')
{
fprintf(stderr, "Bad check-state level: %s\n", pValue);
return hrError;
}
check_state_level = n;
break;
}
case cGobbleFDs:
{
int n;
n = strtol(pValue, (char **)0, 0);
while(--n >= 0) {
(void)dup(2);
}
break;
}
#endif
#ifdef CHECK_STRINGS
case cCheckStrings:
check_string_table_flag = MY_TRUE;
break;
#endif
case cFuncall:
/* Store the value in a list for later evaluation */
{
FData * fdata;
fdata = malloc(sizeof(*fdata) + strlen(pValue));
if (!fdata)
{
fprintf(stderr, "Out of memory for '-f %s'.\n", pValue);
return hrError;
}
fdata->next = NULL;
strcpy(fdata->txt, pValue);
if (f_tail)
f_tail->next = fdata;
f_tail = fdata;
if (!f_head)
f_head = fdata;
}
break;
default:
/* This shouldn't happen. */
fprintf(stderr, "%s driver: (eval_arg) Internal error, eOption is %d\n"
, time_stamp(), eOption);
return hrError;
} /* switch */
return hrSuccess;
} /* eval_arg() */
/*-------------------------------------------------------------------------*/
static Bool
open_arg_file (InputSource ** ppInput, const char * pName)
/* Try to open the file <pName> as new input source.
* If successful, add the source to the list starting at *ppInput and
* return MY_TRUE.
* On failure, print an error message and return MY_FALSE.
*/
{
InputSource * pSrc;
size_t size, left;
FILE * f;
struct stat st;
char * pData;
/* Auxiliary structure to store the found argument words. */
typedef struct Marker {
struct Marker * next;
char * pArg;
} Marker;
Marker * mHead = NULL;
Marker * mTail = NULL;
/* If this is a nested include, make sure that we don't get caught
* in a recursion.
*/
for (pSrc = *ppInput; pSrc && pSrc->name != NULL; pSrc = pSrc->next)
{
if (!strcmp(pSrc->name, pName))
{
fprintf(stderr
, "driver: Recursion in nested argument files: %s\n"
, pName);
for (pSrc = *ppInput; pSrc && pSrc->name != NULL; pSrc = pSrc->next)
fprintf(stderr, " included by %s\n", pSrc->name);
return MY_FALSE;
}
}
/* If the file would be opened in text mode, the size from fstat would
* not match the number of characters that we can read.
*/
f = fopen(pName, "rb");
if (f == NULL)
{
int err = errno;
fprintf( stderr
, "driver: Can't open argument file '%s' for reading: (%d) %s\n"
, pName, err, strerror(err)
);
return MY_FALSE;
}
/* Check if the file is small enough to be read. */
if (fstat(fileno(f), &st) == -1)
{
int err = errno;
fprintf( stderr
, "driver: Can't stat argument file '%s': (%d) %s\n"
, pName, err, strerror(err)
);
fclose(f);
return MY_FALSE;
}
size = (size_t)st.st_size;
/* Get a new input source structure and read in the file. */
{
size_t len = strlen(pName);
pSrc = malloc(sizeof(*pSrc) + len + 1 + size);
if (pSrc == NULL)
{
fprintf( stderr
, "driver: Out of memory reading argument file '%s'\n"
, pName
);
fclose(f);
return MY_FALSE;
}
pSrc->arg = 0;
pSrc->argc = 0;
pSrc->argv = NULL;
pSrc->next = NULL;
strcpy(pSrc->data, pName);
pSrc->data[len] = '\0';
pSrc->name = pSrc->data;
pData = &(pSrc->data[len+1]);
if (1 != fread(pData, size, 1, f))
{
int err = errno;
fprintf( stderr
, "driver: Error reading argument file '%s': (%d) %s\n"
, pName, err, strerror(err)
);
free(pSrc);
fclose(f);
return MY_FALSE;
}
/* Ensure a terminating 0 */
pData[size] = '\0';
}
fclose(f);
/* Now scan the read data and search for words.
* Store the found words in the Marker list, and insert the
* \0 terminators.
*/
for (left = 0; left < size && *pData != '\0'; left++, pData++)
{
Marker *pMarker;
Bool endFound, quoted;
char c = *pData;
if (isascii((unsigned char)c) && (isspace((unsigned char)c)
|| c == '\r' || c == '\n'))
continue;
/* Found a non-space. If it is a '#', it is a comment - skip it. */
if (c == '#')
{
for ( left++, pData++
; left < size && *pData != '\0'
; left++, pData++)
if (*pData == '\r' || *pData == '\n')
break;
/* pData now points to the lineend character, or to the end
* of the file. Either way, continuing the outer loop will
* do the right thing.
*/
continue;
}
/* It is a true new word. Store it's starting position in
* a new marker. Oh, and count it.
*/
pSrc->argc++;
pMarker = alloca(sizeof(*pMarker));
if (pMarker == NULL)
{
fprintf( stderr
, "driver: Out of memory reading argument file '%s'\n"
, pName
);
free(pSrc);
return MY_FALSE;
}
pMarker->pArg = pData;
pMarker->next = NULL;
if (mTail)
mTail->next = pMarker;
mTail = pMarker;
if (!mHead)
mHead = pMarker;
/* Now search for the end of the word.
* Look at the first character again in case it's a quote.
*/
for ( endFound = MY_FALSE, quoted = MY_FALSE
; left < size && *pData != '\0'
; left++, pData++
)
{
c = *pData;
/* Line end always terminates the search */
if (c == '\r' || c == '\n')
{
if (quoted)
{
fprintf( stderr
, "driver: Error in argument file '%s': "
"Quoted string spans more than one line.\n"
, pName
);
free(pSrc);
return MY_FALSE;
}
endFound = MY_TRUE;
break;
}
/* Space outside of a quoted string also terminates */
if (!quoted
&& isascii((unsigned char)c)
&& isspace((unsigned char)c))
{
endFound = MY_TRUE;
break;
}
/* If it's a '"', toggle the quoted flag. */
if (c == '"')
quoted = !quoted;
/* Anyway, it's not a space here, so continue the search */
}
/* One possible error: end of file in a quoted string */
if (!endFound && quoted)
{
fprintf( stderr
, "driver: Error in argument file '%s': "
"Unexpected end of file in quoted string.\n"
, pName
);
free(pSrc);
return MY_FALSE;
}
/* pData now points to the first character after the found word
* It is a space (or the terminating '\0' at the end of the file),
* so we can put the '\0' for the argument word in it.
*/
*pData = '\0';
}
/* We found all the arguments. Now setup an argv array. */
if (pSrc->argc == 0)
{
fprintf( stderr
, "driver: Warning: Argument file '%s' contained no data.\n"
, pName);
/* We will setup an empty InputSource - no need to abort argument
* parsing because of this.
*/
}
{
int i;
pSrc->argv = malloc(sizeof(pSrc->argv[0]) * (pSrc->argc+1));
if (pSrc->argv == NULL)
{
fprintf( stderr
, "driver: Out of memory reading argument file '%s'\n"
, pName
);
free(pSrc);
return MY_FALSE;
}
for (i = 0; i < pSrc->argc; i++, mHead = mHead->next)
{
pSrc->argv[i] = mHead->pArg;
}
pSrc->argv[pSrc->argc] = NULL; /* Just in case */
}
/* Link the now complete InputSource structure into the list */
pSrc->next = *ppInput;
*ppInput = pSrc;
return MY_TRUE;
} /* open_arg_file() */
/*-------------------------------------------------------------------------*/
static INLINE void
free_sources (InputSource * pInput)
/* <pInput> points at the head of a list of input sources.
* Deallocate all of them.
*/
{
while (pInput != NULL)
{
InputSource * pSrc = pInput;
pInput = pInput->next;
if (pSrc->name != NULL && pSrc->argv)
/* Not the commandline */
free(pSrc->argv);
free(pSrc);
}
} /* free_sources() */
/*-------------------------------------------------------------------------*/
static int
getargs (int argc, char ** argv, int (*opt_eval)(int, const char *) )
/* Get the arguments from the commandline and pass them
* as (number, optional value) to the opt_eval callback.
* If opt_eval() returns hrError, argument scanning is terminated.
* In that case, or if getargs() detects an error itself, getargs() returns
* non-zero.
* A zero return means 'success' in both cases.
*/
{
int i; /* all purpose */
int iArg; /* Number of argument under inspection */
OptNumber eOption; /* The current recognized option */
int iOption; /* The index of the recognized option */
short bCont; /* True: find another option in the same argument */
short bDone; /* True: all options parsed, only args left */
short bShort; /* True: current argument is a short option */
char * pArg; /* Next argument character to consider */
InputSource * pInput; /* Head of list of input sources */
/* Make the compiler happy */
bShort = MY_FALSE;
pArg = NULL;
/* Set up the basic input source with the commandline arguments */
pInput = malloc(sizeof(*pInput));
if (!pInput)
{
fputs("driver: Out of memory.\n", stderr);
return 1;
}
pInput->argc = argc-1;
pInput->argv = argv+1;
pInput->arg = 0;
pInput->name = NULL;
pInput->next = NULL;
/* Scan arguments from all input sources.
*/
while (pInput != NULL)
{
/* Scan all arguments */
bCont = MY_FALSE;
bDone = MY_FALSE;
for (iArg = pInput->arg; iArg < pInput->argc; !bCont ? iArg++ : iArg)
{
size_t iArglen; /* Length of remaining argument */
char * pValue; /* First character of an option value, or NULL */
int bTakesValue; /* This option takes a value */
/* Make the compiler happy */
iArglen = 0;
pValue = NULL;
bTakesValue = MY_FALSE;
if (bDone)
eOption = cArgument;
else
/* If this is not a continuation, reinitialise the inspection vars.
* For --opt=val arguments, pValue is set to the first character of val.
*/
if (!bCont)
{
pArg = pInput->argv[iArg];
if ('-' == pArg[0] && '-' == pArg[1]) /* Long option? */
{
eOption = cUnknown;
bShort = MY_FALSE;
pArg += 2;
/* Special case: if the argument is just '--', it marks the
* end of all options.
* We set a flag and continue with the next argument.
*/
if ('\0' == *pArg)
{
bDone = MY_TRUE;
continue;
}
pValue = strchr(pArg, '=');
if (pValue != NULL)
{
iArglen = (size_t)(pValue - pArg);
pValue++;
}
else
iArglen = strlen(pArg);
}
else if ('-' == pArg[0]) /* Short option? */
{
eOption = cUnknown;
bShort = MY_TRUE;
pArg++;
iArglen = strlen(pArg);
pValue = NULL;
}
else /* No option */
{
eOption = cArgument;
pValue = pArg;
iArglen = 0;
}
}
else
eOption = cUnknown;
/* If the option is not determined yet, do it.
* Set pValue to the first character of the value if any.
*/
if (cUnknown == eOption)
{
if (bShort) /* search the short option */
{
for (iOption = 0; iOption < sizeof(aShortOpts) / sizeof(aShortOpts[0]); iOption++)
{
if (*pArg == aShortOpts[iOption].cOption)
{
eOption = (OptNumber)aShortOpts[iOption].eNumber;
bTakesValue = aShortOpts[iOption].bValue;
pArg++; iArglen--; /* Consume this character */
break;
}
}
/* Consume a following '=' if appropriate */
if (cUnknown != eOption && bTakesValue && iArglen > 0 && '=' == *pArg)
{
pArg++; iArglen--;
}
/* If there is a value following in the same argument, set pValue to
* it and mark the remaining characters as 'consumed'
*/
if (cUnknown != eOption && bTakesValue && iArglen > 0)
{
pValue = pArg;
pArg += iArglen;
iArglen = 0;
}
}
else /* search the long option */
{
for (iOption = 0; iOption < sizeof(aLongOpts) / sizeof(aLongOpts[0]); iOption++)
if (iArglen == strlen(aLongOpts[iOption].pOption)
&& !strncasecmp(pArg, aLongOpts[iOption].pOption, iArglen))
{
eOption = (OptNumber)aLongOpts[iOption].eNumber;
bTakesValue = aLongOpts[iOption].bValue;
break;
}
}
if (cUnknown == eOption)
{
if (pInput->name != NULL)
fprintf(stderr, "driver: (%s) Unknown option '", pInput->name);
else
fputs("driver: Unknown option '", stderr);
if (bShort)
fprintf(stderr, "-%c", *pArg);
else
fprintf(stderr, "--%*.*s", (int)iArglen, (int)iArglen, pArg);
fputs("'.\n", stderr);
free_sources(pInput);
return 1;
}
/* If at this point bTakesValue is true, but pValue is still NULL,
* then the value is in the next argument. Get it if it's there.
*/
if (bTakesValue && pValue == NULL && iArg + 1 < pInput->argc)
{
iArg++;
pValue = pInput->argv[iArg];
}
/* Signal an error if pValue is still NULL or if it's empty. */
if (bTakesValue && (pValue == NULL || !strlen(pValue)))
{
fputs("driver: Option '", stderr);
if (bShort)
putc((unsigned char)(aShortOpts[iOption].cOption), stderr);
else
fputs(aLongOpts[iOption].pOption, stderr);
fputs("' expects a value.\n", stderr);
free_sources(pInput);
return 1;
}
} /* if (unknown option) */
/* Before evaluation of the parsed option, determine 'bCont' */
bCont = bShort && (iArglen > 0) && !bTakesValue;
/* --- The option evaluation --- */
i = (*opt_eval)(eOption, pValue);
if (i == hrError)
{
free_sources(pInput);
return 1;
}
if (i == hrArgFile)
{
pInput->arg = iArg+1;
if (!open_arg_file(&pInput, pValue))
return 1;
/* We have a new input source here, so reset the loop
* parameters.
*/
iArg = -1;
bCont = MY_FALSE;
bDone = MY_FALSE;
}
} /* for (iArg) */
/* We are done with this input source - free it */
{
InputSource * pSrc = pInput;
pInput = pInput->next;
pSrc->next = NULL;
free_sources(pSrc);
}
} /* while(pInput) */
return 0;
} /* getargs() */
/***************************************************************************/