#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#ifdef DEBUG
#include <mach/mach.h>
#include <mach/mach_error.h>
#endif
#include "scutil.h"
#include "SCDPrivate.h"
#include "commands.h"
#include "dictionary.h"
#define LINE_LENGTH 256
SCDSessionRef session = NULL;
SCDHandleRef data = NULL;
int nesting = 0;
CFMutableArrayRef sources = NULL;
static char *
getLine(char *buf, int len, FILE *fp)
{
int x;
if (fgets(buf, len, fp) == NULL)
return NULL;
x = strlen(buf);
if (buf[x-1] == '\n') {
buf[x-1] = '\0';
} else {
do {
x = fgetc(fp);
} while ((x != '\n') && (x != EOF));
}
return buf;
}
char *
getString(char **line)
{
char *s, *e, c, *string;
int i, isQuoted = 0, escaped = 0;
if (*line == NULL) return NULL;
if (**line == '\0') return NULL;
while (isspace(**line)) *line += 1;
s = *line;
if (*s == '\0') {
return NULL;
} else if (*s == '"') {
isQuoted = 1;
s++;
}
for (e = s; (c = *e) != '\0'; e++) {
if (isQuoted && (c == '"'))
break;
if (c == '\\') {
e++;
if (*e == '\0')
break;
if ((*e == '"') || isspace(*e))
escaped++;
}
if (!isQuoted && isspace(c))
break;
}
string = malloc(e - s - escaped + 1);
for (i = 0; s < e; s++) {
string[i] = *s;
if (!((s[0] == '\\') && ((s[1] == '"') || isspace(s[1])))) i++;
}
string[i] = '\0';
if (isQuoted)
e++;
*line = e;
return string;
}
boolean_t
process_line(FILE *fp)
{
char line[LINE_LENGTH], *s, *arg, **argv = NULL;
int i, argc;
if (getLine(line, sizeof(line), fp) == NULL)
return FALSE;
if ((nesting > 0) && SCDOptionGet(NULL, kSCDOptionVerbose)) {
SCDLog(LOG_NOTICE, CFSTR("%d> %s"), nesting, line);
}
if (strcasecmp(line, "exit") == 0) return FALSE;
if (strcasecmp(line, "quit") == 0) return FALSE;
if (strcasecmp(line, "q" ) == 0) return FALSE;
s = line;
argc = 0;
while ((arg = getString(&s)) != NULL) {
if (argc == 0)
argv = (char **)malloc(2 * sizeof(char *));
else
argv = (char **)realloc(argv, ((argc + 2) * sizeof(char *)));
argv[argc++] = arg;
}
if (argc > 0) {
argv[argc] = NULL;
if (*argv[0] != '#')
do_command(argc, argv);
for (i = 0; i < argc; i++)
free(argv[i]);
free(argv);
}
return TRUE;
}
void
runLoopProcessInput(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
FILE *fp = info;
if (process_line(fp) == FALSE) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
(CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
kCFRunLoopDefaultMode);
CFSocketInvalidate(s);
CFArrayRemoveValueAtIndex(sources, 0);
if (CFArrayGetCount(sources) > 0) {
CFRunLoopAddSource(CFRunLoopGetCurrent(),
(CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
kCFRunLoopDefaultMode);
} else {
exit (EX_OK);
}
nesting--;
}
if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
_showMachPortStatus();
if ((CFArrayGetCount(sources) == 1) && isatty(STDIN_FILENO)) {
printf("> ");
fflush(stdout);
}
}
return;
}
int
main(int argc, const char *argv[])
{
extern int optind;
int opt;
boolean_t ok;
SCDOptionSet(NULL, kSCDOptionUseCFRunLoop, FALSE);
while ((opt = getopt(argc, argv, "dvr")) != -1)
switch(opt) {
case 'd':
SCDOptionSet(NULL, kSCDOptionDebug, TRUE);
break;
case 'v':
SCDOptionSet(NULL, kSCDOptionVerbose, TRUE);
break;
case 'r':
SCDOptionSet(NULL, kSCDOptionUseCFRunLoop, TRUE);
break;
case '?':
default :
do_help(0, NULL);
}
argc -= optind;
argv += optind;
do_dictInit(0, NULL);
if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
CFSocketRef in;
CFSocketContext context = { 0, stdin, NULL, NULL, NULL };
CFRunLoopSourceRef rls;
in = CFSocketCreateWithNative(NULL,
STDIN_FILENO,
kCFSocketReadCallBack,
runLoopProcessInput,
&context);
rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
sources = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(sources, rls);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
CFRelease(in);
}
do {
_showMachPortStatus();
if (isatty(STDIN_FILENO)) {
printf("> ");
fflush(stdout);
}
if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
CFRunLoopRun();
ok = FALSE;
} else {
ok = process_line(stdin);
}
} while (ok);
exit (EX_OK); return 0; }