#include <debug.h>
#include <cmdline.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <ctype.h>
extern char *optarg;
extern int optind, opterr, optopt;
static struct option long_options[] = {
{"start-address", required_argument, 0, 's'},
{"inc-address", required_argument, 0, 'i'},
{"locals-only", no_argument, 0, 'l'},
{"quiet", no_argument, 0, 'Q'},
{"noupdate", no_argument, 0, 'n'},
{"lookup", required_argument, 0, 'L'},
{"default", required_argument, 0, 'D'},
{"verbose", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{"mapfile", required_argument, 0, 'M'},
{"output", required_argument, 0, 'o'},
{"prelinkmap", required_argument, 0, 'p'},
{0, 0, 0, 0},
};
/* This array must parallel long_options[] */
static const char *descriptions[] = {
"start address to prelink libraries to",
"address increment for each library",
"prelink local relocations only",
"suppress informational and non-fatal error messages",
"do a dry run--calculate the prelink info but do not update any files",
"provide a directory for library lookup",
"provide a default library or executable for symbol lookup",
"print verbose output",
"print help screen",
"print a list of prelink addresses to file (prefix filename with + to append instead of overwrite)",
"specify an output directory (if multiple inputs) or file (is single input)",
"specify a file with prelink addresses instead of a --start-address/--inc-address combination",
};
void print_help(const char *name) {
fprintf(stdout,
"invokation:\n"
"\t%s file1 [file2 file3 ...] -Ldir1 [-Ldir2 ...] -saddr -iinc [-Vqn] [-M<logfile>]\n"
"\t%s -l file [-Vqn] [-M<logfile>]\n"
"\t%s -h\n\n", name, name, name);
fprintf(stdout, "options:\n");
struct option *opt = long_options;
const char **desc = descriptions;
while (opt->name) {
fprintf(stdout, "\t-%c/--%s%s: %s\n",
opt->val,
opt->name,
(opt->has_arg ? " (argument)" : ""),
*desc);
opt++;
desc++;
}
}
int get_options(int argc, char **argv,
int *start_addr,
int *inc_addr,
int *locals_only,
int *quiet,
int *dry_run,
char ***dirs,
int *num_dirs,
char ***defaults,
int *num_defaults,
int *verbose,
char **mapfile,
char **output,
char **prelinkmap) {
int c;
ASSERT(dry_run); *dry_run = 0;
ASSERT(quiet); *quiet = 0;
ASSERT(verbose); *verbose = 0;
ASSERT(dirs); *dirs = NULL;
ASSERT(num_dirs); *num_dirs = 0;
ASSERT(defaults); *defaults = NULL;
ASSERT(num_defaults); *num_defaults = 0;
ASSERT(start_addr); *start_addr = -1;
ASSERT(inc_addr); *inc_addr = -1;
ASSERT(locals_only); *locals_only = 0;
ASSERT(mapfile); *mapfile = NULL;
ASSERT(output); *output = NULL;
ASSERT(prelinkmap); *prelinkmap = NULL;
int dirs_size = 0;
int defaults_size = 0;
while (1) {
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long (argc, argv,
"VhnQlL:D:s:i:M:o:p:",
long_options,
&option_index);
/* Detect the end of the options. */
if (c == -1) break;
if (isgraph(c)) {
INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
}
#define SET_STRING_OPTION(name) do { \
ASSERT(optarg); \
(*name) = strdup(optarg); \
} while(0)
#define SET_REPEATED_STRING_OPTION(arr, num, size) do { \
if (*num == size) { \
size += 10; \
*arr = (char **)REALLOC(*arr, size * sizeof(char *)); \
} \
SET_STRING_OPTION(((*arr) + *num)); \
(*num)++; \
} while(0)
#define SET_INT_OPTION(val) do { \
ASSERT(optarg); \
if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \
FAILIF(1 != sscanf(optarg+2, "%x", val), \
"Expecting a hexadecimal argument!\n"); \
} else { \
FAILIF(1 != sscanf(optarg, "%d", val), \
"Expecting a decimal argument!\n"); \
} \
} while(0)
switch (c) {
case 0:
/* If this option set a flag, do nothing else now. */
if (long_options[option_index].flag != 0)
break;
INFO ("option %s", long_options[option_index].name);
if (optarg)
INFO (" with arg %s", optarg);
INFO ("\n");
break;
case 'Q': *quiet = 1; break;
case 'n': *dry_run = 1; break;
case 'M':
SET_STRING_OPTION(mapfile);
break;
case 'o':
SET_STRING_OPTION(output);
break;
case 'p':
SET_STRING_OPTION(prelinkmap);
break;
case 's':
SET_INT_OPTION(start_addr);
break;
case 'i':
SET_INT_OPTION(inc_addr);
break;
case 'L':
SET_REPEATED_STRING_OPTION(dirs, num_dirs, dirs_size);
break;
case 'D':
SET_REPEATED_STRING_OPTION(defaults, num_defaults, defaults_size);
break;
case 'l': *locals_only = 1; break;
case 'h': print_help(argv[0]); exit(1); break;
case 'V': *verbose = 1; break;
case '?':
/* getopt_long already printed an error message. */
break;
#undef SET_STRING_OPTION
#undef SET_REPEATED_STRING_OPTION
#undef SET_INT_OPTION
default:
FAILIF(1, "Unknown option");
}
}
return optind;
}