#include "SecurityTool.h"
#include <SecurityTool/readline.h>
#include "leaks.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <CoreFoundation/CFRunLoop.h>
#if NO_SERVER
#include <securityd/spi.h>
#endif
#define MAX_LINE_LEN 4096
#define MAX_ARGS 32
const char *prompt_string = "security> ";
const char *prog_name;
int do_quiet = 0;
int do_verbose = 0;
static int
match_command(const char *command_name, const char *name)
{
return !strncmp(command_name, name, strlen(name));
}
int
help(int argc, char * const *argv)
{
const command *c;
if (argc > 1)
{
char * const *arg;
for (arg = argv + 1; *arg; ++arg)
{
int found = 0;
for (c = commands; c->c_name; ++c)
{
if (match_command(c->c_name, *arg))
{
found = 1;
break;
}
}
if (found)
printf("Usage: %s %s\n", c->c_name, c->c_usage);
else
{
fprintf(stderr, "%s: no such command: %s", argv[0], *arg);
return 1;
}
}
}
else
{
for (c = commands; c->c_name; ++c)
printf(" %-17s %s\n", c->c_name, c->c_help);
}
return 0;
}
typedef enum
{
SKIP_WS,
READ_ARG,
READ_ARG_ESCAPED,
QUOTED_ARG,
QUOTED_ARG_ESCAPED
} parse_state;
static void
split_line(char *line, int *pargc, char * const **pargv)
{
static char *argvec[MAX_ARGS + 1];
int argc = 0;
char *ptr = line;
char *dst = line;
parse_state state = SKIP_WS;
int quote_ch = 0;
for (ptr = line; *ptr; ++ptr)
{
if (state == SKIP_WS)
{
if (isspace(*ptr))
continue;
if (*ptr == '"' || *ptr == '\'')
{
quote_ch = *ptr;
state = QUOTED_ARG;
argvec[argc] = dst;
continue;
}
else
{
state = READ_ARG;
argvec[argc] = dst;
}
}
if (state == READ_ARG)
{
if (*ptr == '\\')
{
state = READ_ARG_ESCAPED;
continue;
}
else if (isspace(*ptr))
{
*dst++ = '\0';
argc++;
state = SKIP_WS;
if (argc >= MAX_ARGS)
break;
}
else
*dst++ = *ptr;
}
if (state == QUOTED_ARG)
{
if (*ptr == '\\')
{
state = QUOTED_ARG_ESCAPED;
continue;
}
if (*ptr == quote_ch)
{
*dst++ = '\0';
argc++;
state = SKIP_WS;
if (argc >= MAX_ARGS)
break;
}
else
*dst++ = *ptr;
}
if (state == READ_ARG_ESCAPED)
{
*dst++ = *ptr;
state = READ_ARG;
}
if (state == QUOTED_ARG_ESCAPED)
{
*dst++ = *ptr;
state = QUOTED_ARG;
}
}
if (state != SKIP_WS)
{
*dst++ = '\0';
argc++;
}
argvec[argc] = NULL;
*pargv = argvec;
*pargc = argc;
}
static int
usage(void)
{
printf(
"Usage: %s [-h] [-i] [-l] [-p prompt] [-q] [-v] [command] [opt ...]\n"
" -i Run in interactive mode.\n"
" -l Run /usr/bin/leaks -nocontext before exiting.\n"
" -p Set the prompt to \"prompt\" (implies -i).\n"
" -q Be less verbose.\n"
" -v Be more verbose about what's going on.\n"
"%s commands are:\n", prog_name, prog_name);
help(0, NULL);
return 2;
}
static int
execute_command(int argc, char * const *argv)
{
const command *c;
int found = 0;
if (argc == 0)
return 0;
for (c = commands; c->c_name; ++c)
{
if (match_command(c->c_name, argv[0]))
{
found = 1;
break;
}
}
if (found)
{
int result;
optind = 1;
optreset = 1;
if (do_verbose)
{
int ix;
fprintf(stderr, "%s", c->c_name);
for (ix = 1; ix < argc; ++ix)
fprintf(stderr, " \"%s\"", argv[ix]);
fprintf(stderr, "\n");
}
result = c->c_func(argc, argv);
if (result == 2)
fprintf(stderr, "Usage: %s %s\n %s\n", c->c_name, c->c_usage, c->c_help);
return result;
}
else
{
fprintf(stderr, "unknown command \"%s\"", argv[0]);
return 1;
}
}
static void
receive_notifications(void)
{
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource);
}
int
main(int argc, char * const *argv)
{
int result = 0;
int do_help = 0;
int do_interactive = 0;
int do_leaks = 0;
int ch;
prog_name = strrchr(argv[0], '/');
prog_name = prog_name ? prog_name + 1 : argv[0];
optind = 1;
optreset = 1;
while ((ch = getopt(argc, argv, "hilp:qv")) != -1)
{
switch (ch)
{
case 'h':
do_help = 1;
break;
case 'i':
do_interactive = 1;
break;
case 'l':
do_leaks = 1;
break;
case 'p':
do_interactive = 1;
prompt_string = optarg;
break;
case 'q':
do_quiet = 1;
break;
case 'v':
do_verbose = 1;
break;
case '?':
default:
return usage();
}
}
argc -= optind;
argv += optind;
#if NO_SERVER
# if DEBUG
# endif
#endif
if (do_help)
{
return help(argc + 1, argv - 1);
}
else if (argc > 0)
{
receive_notifications();
result = execute_command(argc, argv);
receive_notifications();
}
else if (do_interactive)
{
int show_prompt = isatty(0);
for (;;)
{
static char buffer[MAX_LINE_LEN];
char * const *av, *input;
int ac;
if (show_prompt)
fprintf(stderr, "%s", prompt_string);
input = readline(buffer, MAX_LINE_LEN);
if (!input)
break;
split_line(input, &ac, &av);
receive_notifications();
result = execute_command(ac, av);
receive_notifications();
if (result == -1)
{
result = 0;
break;
}
if (result && ! do_quiet)
{
fprintf(stderr, "%s: returned %d\n", av[0], result);
}
}
}
else
result = usage();
if (do_leaks)
{
char *const argvec[3] = { "leaks", "-nocontext", NULL };
leaks(2, argvec);
}
return result;
}