655 lines
13 KiB
C
655 lines
13 KiB
C
/* Copyright 1995, 1996 by Abacus Research and
|
|
* Development, Inc. All rights reserved.
|
|
*/
|
|
|
|
#if !defined (OMIT_RCSID_STRINGS)
|
|
char ROMlib_rcsid_option[] =
|
|
"$Id: option.c 63 2004-12-24 18:19:43Z ctm $";
|
|
#endif
|
|
|
|
#include "rsys/common.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "rsys/flags.h"
|
|
#include "rsys/option.h"
|
|
#include "rsys/parsenum.h"
|
|
#include "rsys/notmac.h"
|
|
|
|
#ifdef NEXTSTEP
|
|
|
|
#ifndef OPENSTEP
|
|
|
|
#include <defaults/defaults.h>
|
|
|
|
#else /* OPENSTEP */
|
|
|
|
#include <Foundation/NSUserDefaults.h>
|
|
#include <Foundation/NSString.h>
|
|
|
|
#endif /* OPENSTEP */
|
|
|
|
#endif /* NEXTSTEP */
|
|
|
|
struct opt_block
|
|
{
|
|
char *interface;
|
|
option_t *opts;
|
|
int n_opts;
|
|
} *opt_blocks;
|
|
static int n_opt_blocks_max;
|
|
static int n_opt_blocks;
|
|
|
|
static int max_pre_notes;
|
|
static int n_pre_notes;
|
|
static char **pre_notes;
|
|
|
|
static char *wrap_buf;
|
|
static int wrap_buf_size;
|
|
|
|
static int help_buf_len, help_buf_len_max;
|
|
static char *help_buf;
|
|
|
|
void
|
|
opt_init (void)
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
opt_shutdown (void)
|
|
{
|
|
free (wrap_buf);
|
|
free (help_buf);
|
|
free (pre_notes);
|
|
free (opt_blocks);
|
|
}
|
|
|
|
void
|
|
strcpy_to_wrap_buf (char *text)
|
|
{
|
|
int text_len;
|
|
char *src, *dst;
|
|
|
|
text_len = strlen (text) + 1;
|
|
|
|
if (wrap_buf_size < text_len)
|
|
{
|
|
wrap_buf = realloc (wrap_buf, text_len * sizeof *wrap_buf);
|
|
wrap_buf_size = text_len;
|
|
}
|
|
for (dst = wrap_buf, src = text; *src;)
|
|
{
|
|
if (*src != '\n')
|
|
*dst ++ = *src ++;
|
|
else
|
|
src ++;
|
|
}
|
|
*dst = '\0';
|
|
}
|
|
|
|
static void
|
|
wrap (char *buf,
|
|
int desired_len,
|
|
int *out_len, int *next_len)
|
|
{
|
|
int buf_len;
|
|
|
|
buf_len = strlen (buf);
|
|
|
|
if (buf_len <= desired_len)
|
|
{
|
|
*out_len = buf_len;
|
|
*next_len = -1;
|
|
}
|
|
else
|
|
{
|
|
char *t;
|
|
|
|
t = &buf[desired_len];
|
|
while (!isspace (*t))
|
|
t --;
|
|
*next_len = (t - buf) + 1;
|
|
|
|
while (isspace (*t))
|
|
t --;
|
|
|
|
*out_len = (t - buf) + 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
_safe_strncat (char *dst, char *src, int len)
|
|
{
|
|
char *t;
|
|
int dst_len;
|
|
|
|
dst_len = strlen (dst);
|
|
t = &dst[dst_len];
|
|
|
|
memcpy (t, src, len);
|
|
|
|
dst[dst_len + len] = '\0';
|
|
}
|
|
|
|
void
|
|
send_to_help_buf (char *text, int len, int append_newline_p)
|
|
{
|
|
if (help_buf == NULL)
|
|
{
|
|
help_buf = malloc (2 * len);
|
|
help_buf_len_max = 2 * len;
|
|
|
|
*help_buf = '\0';
|
|
help_buf_len = 1;
|
|
}
|
|
else if (help_buf_len + len > help_buf_len_max)
|
|
{
|
|
help_buf_len_max *= 2;
|
|
help_buf = realloc (help_buf, help_buf_len_max * sizeof *help_buf);
|
|
}
|
|
|
|
_safe_strncat (help_buf, text, len);
|
|
help_buf_len += len;
|
|
if (append_newline_p)
|
|
{
|
|
strcat (help_buf, "\n");
|
|
help_buf_len += len;
|
|
}
|
|
}
|
|
|
|
void
|
|
_generate_help_message (void)
|
|
{
|
|
int i, block_i;
|
|
char *buf;
|
|
|
|
for (i = 0; i < n_pre_notes; i ++)
|
|
{
|
|
int out_len, next_len;
|
|
char *pre_note = pre_notes[i];
|
|
|
|
strcpy_to_wrap_buf (pre_note);
|
|
|
|
for (buf = wrap_buf;; buf += next_len)
|
|
{
|
|
wrap (buf, 75, &out_len, &next_len);
|
|
send_to_help_buf (buf, out_len, TRUE);
|
|
if (next_len == -1)
|
|
break;
|
|
}
|
|
}
|
|
/* newline to separate pre-notes and options */
|
|
send_to_help_buf ("", 0, TRUE);
|
|
for (block_i = 0; block_i < n_opt_blocks; block_i ++)
|
|
{
|
|
char *interface = opt_blocks[block_i].interface;
|
|
option_t *opts = opt_blocks[block_i].opts;
|
|
int n_opts = opt_blocks[block_i].n_opts;
|
|
int opt_i;
|
|
|
|
send_to_help_buf (interface, strlen (interface), FALSE);
|
|
send_to_help_buf (":", 1, TRUE);
|
|
|
|
for (opt_i = 0; opt_i < n_opts; opt_i ++)
|
|
{
|
|
char *spaces = " ";
|
|
int next_len, out_len;
|
|
|
|
option_t *opt = &opts[opt_i];
|
|
int opt_text_len;
|
|
int same_line_p;
|
|
|
|
if (!opt->desc)
|
|
continue;
|
|
|
|
opt_text_len = strlen (opt->text);
|
|
same_line_p = opt_text_len < 14;
|
|
|
|
send_to_help_buf (" -", 3, FALSE);
|
|
send_to_help_buf (opt->text, opt_text_len,
|
|
!same_line_p);
|
|
if (same_line_p)
|
|
send_to_help_buf (spaces,
|
|
20 - (3 + opt_text_len), FALSE);
|
|
else
|
|
send_to_help_buf (spaces, 20, FALSE);
|
|
|
|
strcpy_to_wrap_buf (opt->desc);
|
|
for (buf = wrap_buf;; buf += next_len)
|
|
{
|
|
wrap (buf, 55, &out_len, &next_len);
|
|
send_to_help_buf (buf, out_len, TRUE);
|
|
if (next_len != -1)
|
|
send_to_help_buf (spaces, 20, FALSE);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
char *
|
|
opt_help_message (void)
|
|
{
|
|
if (help_buf == NULL)
|
|
_generate_help_message ();
|
|
return help_buf;
|
|
}
|
|
|
|
void
|
|
opt_register_pre_note (char *note)
|
|
{
|
|
if (!pre_notes)
|
|
{
|
|
n_pre_notes = 0;
|
|
max_pre_notes = 4;
|
|
pre_notes = malloc (max_pre_notes * sizeof *pre_notes);
|
|
}
|
|
if (n_pre_notes == max_pre_notes)
|
|
{
|
|
max_pre_notes *= 2;
|
|
pre_notes = realloc (pre_notes, max_pre_notes * sizeof *pre_notes);
|
|
}
|
|
pre_notes[n_pre_notes ++] = note;
|
|
}
|
|
|
|
|
|
#if !defined (MSDOS)
|
|
# define ERRMSG_STREAM stderr
|
|
#else
|
|
# define ERRMSG_STREAM stdout /* stderr is annoying to DOS users. */
|
|
#endif
|
|
|
|
|
|
void
|
|
opt_register (char *new_interface,
|
|
option_t *new_opts, int n_new_opts)
|
|
{
|
|
int block_i;
|
|
struct opt_block *block;
|
|
|
|
if (help_buf)
|
|
{
|
|
/* internal error, must register all options before generating
|
|
help message */
|
|
fprintf (ERRMSG_STREAM, "\
|
|
%s: internal options error: opt register after help message generation.\n",
|
|
program_name);
|
|
exit (-16);
|
|
}
|
|
|
|
/* check for conflicting options */
|
|
for (block_i = 0; block_i < n_opt_blocks; block_i ++)
|
|
{
|
|
char *interface = opt_blocks[block_i].interface;
|
|
option_t *opts = opt_blocks[block_i].opts;
|
|
int n_opts = opt_blocks[block_i].n_opts;
|
|
int opt_i, new_opt_i;
|
|
|
|
for (opt_i = 0; opt_i < n_opts; opt_i ++)
|
|
for (new_opt_i = 0; new_opt_i < n_new_opts; new_opt_i ++)
|
|
{
|
|
if (!strcmp (opts[opt_i].text,
|
|
new_opts[new_opt_i].text))
|
|
{
|
|
/* conflicting options */
|
|
fprintf (ERRMSG_STREAM, "\
|
|
%s: opt internal error: `%s' and `%s' both request option `%s'\n",
|
|
program_name,
|
|
interface, new_interface, opts[opt_i].text);
|
|
exit (-16);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!opt_blocks)
|
|
{
|
|
n_opt_blocks = 0;
|
|
n_opt_blocks_max = 4;
|
|
opt_blocks = malloc (n_opt_blocks_max * sizeof *opt_blocks);
|
|
}
|
|
else if (n_opt_blocks == n_opt_blocks_max)
|
|
{
|
|
n_opt_blocks_max *= 2;
|
|
opt_blocks = realloc (opt_blocks, n_opt_blocks_max * sizeof *opt_blocks);
|
|
}
|
|
|
|
block = &opt_blocks[n_opt_blocks ++];
|
|
|
|
block->interface = new_interface;
|
|
block->opts = new_opts;
|
|
block->n_opts = n_new_opts;
|
|
}
|
|
|
|
opt_database_t *
|
|
opt_alloc_db (void)
|
|
{
|
|
opt_database_t *retval;
|
|
|
|
retval = malloc (sizeof *retval);
|
|
|
|
retval->opt_vals = NULL;
|
|
retval->n_opt_vals = 0;
|
|
/* default maximum */
|
|
retval->max_opt_vals = 4;
|
|
|
|
return retval;
|
|
}
|
|
|
|
PRIVATE opt_val_t *
|
|
opt_lookup_helper (opt_database_t *db, char *opt)
|
|
{
|
|
opt_val_t *retval;
|
|
int i;
|
|
|
|
retval = 0;
|
|
|
|
/* try to find this option in the database */
|
|
for (i = 0; i < db->n_opt_vals; i ++)
|
|
{
|
|
if (strcmp (db->opt_vals[i].text, opt) == 0)
|
|
{
|
|
retval = &db->opt_vals[i];
|
|
break;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
opt_val_t *
|
|
opt_lookup (opt_database_t *db, char *opt)
|
|
{
|
|
opt_val_t *retval;
|
|
|
|
retval = opt_lookup_helper (db, opt);
|
|
|
|
#if defined(NEXTSTEP)
|
|
if (!retval || !retval->t_val)
|
|
{
|
|
#ifndef OPENSTEP
|
|
static boolean_t defaults_registered;
|
|
static NXDefaultsVector vec = { { NULL } };
|
|
const char *try;
|
|
#else /* OPENSTEP */
|
|
NSUserDefaults *defaults;
|
|
NSString *try;
|
|
#endif /* OPENSTEP */
|
|
|
|
#ifndef OPENSTEP
|
|
if (!defaults_registered)
|
|
{
|
|
NXRegisterDefaults (ROMlib_appname, vec);
|
|
defaults_registered = TRUE;
|
|
}
|
|
#else /* OPENSTEP */
|
|
defaults = [NSUserDefaults standardUserDefaults];
|
|
try = [defaults stringForKey:[NSString stringWithCString:opt]];
|
|
#endif /* OPENSTEP */
|
|
|
|
#ifndef OPENSTEP
|
|
/* Allow dwrites to work. */
|
|
try = NXGetDefaultValue (ROMlib_appname, opt);
|
|
#endif /* not OPENSTEP */
|
|
if (try)
|
|
{
|
|
if (retval)
|
|
#ifndef OPENSTEP
|
|
retval->val = strcpy (malloc (strlen (try) + 1), try);
|
|
#else /* OPENSTEP */
|
|
{
|
|
char *str;
|
|
int len;
|
|
|
|
len = [try cStringLength];
|
|
str = malloc (len+1);
|
|
[try getCString:str maxLength:len];
|
|
retval->val = str;
|
|
}
|
|
#endif /* OPENSTEP */
|
|
else
|
|
{
|
|
#ifndef OPENSTEP
|
|
opt_put_val (db, opt, try, pri_dwrite, FALSE);
|
|
#else /* OPENSTEP */
|
|
opt_put_val (db, opt, [try cString], pri_dwrite, FALSE);
|
|
#endif /* OPENSTEP */
|
|
retval = opt_lookup_helper (db, opt);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
#ifndef OPENSTEP
|
|
opt_put_val (opt_database_t *db, char *opt, char *val,
|
|
#else /* OPENSTEP */
|
|
opt_put_val (opt_database_t *db, char *opt, const char *val,
|
|
#endif /* OPENSTEP */
|
|
priority_t pri, int temp_val_p)
|
|
{
|
|
opt_val_t *opt_val;
|
|
|
|
opt_val = opt_lookup_helper (db, opt);
|
|
if (!opt_val)
|
|
{
|
|
if (db->opt_vals)
|
|
{
|
|
/* this option is not yet in the database, add it */
|
|
if (db->n_opt_vals == db->max_opt_vals)
|
|
{
|
|
/* allocate some new ones */
|
|
db->max_opt_vals *= 2;
|
|
db->opt_vals = realloc (db->opt_vals,
|
|
(sizeof *(db->opt_vals)
|
|
* db->max_opt_vals));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
db->opt_vals = malloc (sizeof *(db->opt_vals) * db->max_opt_vals);
|
|
}
|
|
opt_val = &db->opt_vals[db->n_opt_vals ++];
|
|
|
|
opt_val->text = opt;
|
|
opt_val->val = NULL;
|
|
opt_val->t_val = NULL;
|
|
}
|
|
|
|
if (temp_val_p)
|
|
{
|
|
opt_val->t_val = val;
|
|
opt_val->t_pri = pri;
|
|
}
|
|
else
|
|
{
|
|
opt_val->val = val;
|
|
opt_val->pri = pri;
|
|
}
|
|
}
|
|
|
|
void
|
|
opt_put_int_val (opt_database_t *db, char *opt, int valint,
|
|
priority_t pri, int temp_val_p)
|
|
{
|
|
char *val, buf[256];
|
|
|
|
sprintf (buf, "%d", valint);
|
|
val = malloc (strlen (buf) + 1);
|
|
strcpy (val, buf);
|
|
|
|
opt_put_val (db, opt, val, pri, temp_val_p);
|
|
}
|
|
|
|
#define option_value(opt_val) ((opt_val)->t_val ?: (opt_val)->val)
|
|
|
|
int
|
|
#ifndef OPENSTEP
|
|
opt_val (opt_database_t *db, char *opt, char **retval)
|
|
#else /* OPENSTEP */
|
|
opt_val (opt_database_t *db, char *opt, const char **retval)
|
|
#endif /* OPENSTEP */
|
|
{
|
|
opt_val_t *opt_val;
|
|
#ifndef OPENSTEP
|
|
char *val = NULL;
|
|
#else /* OPENSTEP */
|
|
const char *val = NULL;
|
|
#endif /* OPENSTEP */
|
|
boolean_t found_p = FALSE;
|
|
|
|
opt_val = opt_lookup (db, opt);
|
|
if (opt_val)
|
|
{
|
|
val = option_value (opt_val);
|
|
if (val)
|
|
{
|
|
if (retval)
|
|
*retval = val;
|
|
found_p = TRUE;
|
|
}
|
|
}
|
|
return found_p;
|
|
}
|
|
|
|
|
|
/* Parses an integer value and returns it in *retval. If
|
|
* parse_error_p is non-NULL, sets parse_error_p to TRUE and prints an
|
|
* error message in case of a parse error, else leaves it untouched.
|
|
* Returns TRUE if a value was found.
|
|
*/
|
|
int
|
|
opt_int_val (opt_database_t *db, char *opt, int *retval,
|
|
boolean_t *parse_error_p)
|
|
{
|
|
opt_val_t *opt_val;
|
|
#ifndef OPENSTEP
|
|
char *val = NULL;
|
|
#else /* OPENSTEP */
|
|
const char *val = NULL;
|
|
#endif /* OPENSTEP */
|
|
|
|
opt_val = opt_lookup (db, opt);
|
|
if (opt_val && (val = option_value (opt_val)) && retval)
|
|
{
|
|
int32 v;
|
|
if (!parse_number (val, &v, 1))
|
|
{
|
|
if (parse_error_p)
|
|
{
|
|
fprintf (stderr, "Malformed numeric argument to -%s.\n", opt);
|
|
*parse_error_p = TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
*retval = v;
|
|
}
|
|
|
|
/* Do *NOT* touch *parse_error_p if there is no error. */
|
|
|
|
return opt_val && val;
|
|
}
|
|
|
|
int
|
|
opt_parse (opt_database_t *db, option_t *opts, int n_opts,
|
|
int *argc, char *argv[])
|
|
{
|
|
int parse_error_p = FALSE;
|
|
int i;
|
|
int argc_left;
|
|
|
|
/* we are sure to copy the null termination because we start with
|
|
`i = 1' */
|
|
argc_left = *argc;
|
|
|
|
/* skip over the program name */
|
|
for (i = 1; i < *argc; i ++, argc_left --)
|
|
{
|
|
char *arg;
|
|
int arg_i;
|
|
|
|
arg_i = i;
|
|
arg = argv[i];
|
|
/* option */
|
|
if (*arg == '-')
|
|
{
|
|
int opt_i;
|
|
|
|
/* find the option among the options */
|
|
for (opt_i = 0; opt_i < n_opts; opt_i ++)
|
|
{
|
|
option_t *opt = &opts[opt_i];
|
|
|
|
if (!strcmp (&arg[1],
|
|
opt->text))
|
|
{
|
|
char *optval = NULL;
|
|
|
|
/* found the option */
|
|
switch (opt->kind)
|
|
{
|
|
case opt_no_arg:
|
|
optval = "1";
|
|
break;
|
|
case opt_optional:
|
|
if ((i + 1) < *argc && *argv[i + 1] != '-')
|
|
{
|
|
optval = argv[++ i];
|
|
argc_left --;
|
|
}
|
|
else
|
|
optval = opt->opt_val;
|
|
break;
|
|
case opt_sticky:
|
|
optval = &arg[1 + strlen (opt->text)];
|
|
break;
|
|
case opt_sep:
|
|
if ((i + 1) < *argc)
|
|
{
|
|
optval = argv[++ i];
|
|
argc_left --;
|
|
}
|
|
else
|
|
{
|
|
fprintf (ERRMSG_STREAM, "\
|
|
%s: option `-%s' requires argument\n",
|
|
program_name, opt->text);
|
|
parse_error_p = TRUE;
|
|
}
|
|
break;
|
|
case opt_ignore:
|
|
goto next_arg;
|
|
case opt_sep_ignore:
|
|
if ((i + 1) < *argc)
|
|
{
|
|
i ++;
|
|
argc_left --;
|
|
}
|
|
else
|
|
{
|
|
fprintf (ERRMSG_STREAM, "\
|
|
%s: option `-%s' requires argument\n",
|
|
program_name, opt->text);
|
|
parse_error_p = TRUE;
|
|
}
|
|
goto next_arg;
|
|
}
|
|
*argc -= (i + 1) - arg_i;
|
|
memmove (&argv[arg_i], &argv[i + 1],
|
|
(argc_left-1) * sizeof *argv);
|
|
i = arg_i - 1;
|
|
opt_put_val (db, opt->text, optval, pri_command_line,
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
next_arg:;
|
|
}
|
|
return parse_error_p;
|
|
}
|