/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Modification History
 *
 * June 1, 2001			Allan Nathanson <ajn@apple.com>
 * - public API conversion
 *
 * November 9, 2000		Allan Nathanson <ajn@apple.com>
 * - initial revision
 */

#include <stdio.h>
#include <string.h>
#include <sys/errno.h>

#include "scutil.h"
#include "commands.h"
#include "dictionary.h"
#include "session.h"
#include "cache.h"
#include "notifications.h"
#include "tests.h"
#include "net.h"

#include "SCDynamicStoreInternal.h"


__private_extern__
const cmdInfo commands_store[] = {
	/* cmd		minArgs	maxArgs	func			group	ctype			*/
	/* 	usage										*/

	{ "help",	0,	0,	do_help,		0,	0,
		" help                          : list available commands"			},

	{ "f.read",	1,	1,	do_readFile,		0,	0,
		" f.read file                   : process commands from file"			},

	{ "quit",	0,	0,	do_quit,		0,	0,
		" quit                          : quit"						},

	{ "q",		0,	0,	do_quit,		0,	-1,
		NULL										},

	{ "exit",	0,	0,	do_quit,		0,	-1,
		NULL										},

	/* local dictionary manipulation commands */

	{ "d.init",	0,	0,	do_dictInit,		1,	0,
		" d.init                        : initialize (empty) dictionary"		},

	{ "d.show",	0,	0,	do_dictShow,		1,	0,
		" d.show                        : show dictionary contents"			},

	{ "d.add",	2,	101,	do_dictSetKey,		1,	0,
		" d.add key [*#?] val [v2 ...]  : add information to dictionary\n"
		"       (*=array, #=number, ?=boolean)"				                },

	{ "d.remove",	1,	1,	do_dictRemoveKey,	1,	0,
		" d.remove key                  : remove key from dictionary"			},

	/* data store manipulation commands */

	{ "open",	0,	1,	do_open,		2,	1,
		" open [\"temporary\"]            : open a session with \"configd\""		},

	{ "close",	0,	0,	do_close,		2,	1,
		" close                         : close current \"configd\" session"		},

	{ "lock",	0,	0,	do_lock,		3,	1,
		" lock                          : secures write access to data store"		},

	{ "unlock",	0,	0,	do_unlock,		3,	1,
		" unlock                        : secures write access to data store"		},

	{ "list",	0,	2,	do_list,		4,	0,
		" list [pattern]                : list keys in data store"			},

	{ "add",	1,	2,	do_add,			4,	0,
		" add key [\"temporary\"]         : add key in data store w/current dict"	},

	{ "get",	1,	1,	do_get,			4,	0,
		" get key                       : get dict from data store w/key"		},

	{ "set",	1,	1,	do_set,			4,	0,
		" set key                         : set key in data store w/current dict"	},

	{ "show",	1,	2,	do_show,		4,	0,
		" show key [\"pattern\"]          : show values in data store w/key"		},

	{ "remove",	1,	1,	do_remove,		4,	0,
		" remove key                    : remove key from data store"			},

	{ "notify",	1,	1,	do_notify,		4,	0,
		" notify key                    : notify key in data store"			},

	{ "touch",	1,	1,	do_touch,		4,	1,
		" touch key                     : touch key in data store"			},

	{ "n.list",	0,	1,	do_notify_list,		5,	0,
		" n.list [\"pattern\"]            : list notification keys"			},

	{ "n.add",	1,	2,	do_notify_add,		5,	0,
		" n.add key [\"pattern\"]         : add notification key"			},

	{ "n.remove",	1,	2,	do_notify_remove,	5,	0,
		" n.remove key [\"pattern\"]      : remove notification key"			},

	{ "n.changes",	0,	0,	do_notify_changes,	5,	0,
		" n.changes                     : list changed keys"				},

	{ "n.watch",	0,	1,	do_notify_watch,	5,	0,
		" n.watch [verbose]             : watch for changes"				},

	{ "n.wait",	0,	0,	do_notify_wait,		5,	2,
		" n.wait                        : wait for changes"				},

	{ "n.callback",	0,	1,	do_notify_callback,	5,	2,
		" n.callback [\"verbose\"]        : watch for changes"				},

	{ "n.signal",	1,	2,	do_notify_signal,	5,	2,
		" n.signal sig [pid]            : signal changes"				},

	{ "n.file",	0,	1,	do_notify_file,		5,	2,
		" n.file [identifier]           : watch for changes via file"			},

	{ "n.cancel",	0,	1,	do_notify_cancel,	5,	0,
		" n.cancel                      : cancel notification requests"			},

	{ "snapshot",	0,	0,	do_snapshot,		99,	2,
		" snapshot                      : save snapshot of store and session data"	}
};
__private_extern__
const int nCommands_store = (sizeof(commands_store)/sizeof(cmdInfo));


__private_extern__
const cmdInfo commands_prefs[] = {
	/* cmd		minArgs	maxArgs	func			group	ctype			*/
	/* 	usage										*/

	{ "help",	0,	0,	do_help,		0,	0,
		" help                          : list available commands"			},

	{ "f.read",	1,	1,	do_readFile,		0,	0,
		" f.read file                   : process commands from file"			},

	{ "quit",	0,	1,	do_net_quit,		0,	0,
		" quit [!]                      : quit"						},

	{ "q",		0,	1,	do_net_quit,		0,	-1,
		NULL										},

	{ "exit",	0,	1,	do_net_quit,		0,	-1,
		NULL										},

	/* network configuration manipulation commands */

	{ "open",	0,	1,	do_net_open,		2,	1,
		" open                          : open the network configuration"		},

	{ "commit",	0,	0,	do_net_commit,		2,	0,
		" commit                        : commit any changes"				},

	{ "apply",	0,	0,	do_net_apply,		2,	0,
		" apply                         : apply any changes"				},

	{ "close",	0,	1,	do_net_close,		2,	1,
		" close [!]                     : close the network configuration"		},

	{ "create",	1,	3, 	do_net_create,		3,	0,
		" create interface <interfaceType> [ <interfaceName> | <interface#> ]\n"
		" create protocol <protocolType>\n"
		" create service [ <interfaceName> | <interface#> [ <serviceName> ]]\n"
		" create set [setName]"								},

	{ "disable",	1,	2,	do_net_disable,		5,	0,
		" disable protocol [ <protocolType> ]\n"
		" disable service  [ <serviceName> | <service#> ]"				},

	{ "enable",	1,	2,	do_net_enable,		4,	0,
		" enable protocol  [ <protocolType> ]\n"
		" enable service   [ <serviceName> | <service#> ]"				},

	{ "remove",	1,	2,	do_net_remove,		6,	0,
		" remove protocol  [ <protocolType> ]\n"
		" remove service   [ <serviceName> | <service#> ]\n"
		" remove set       [ <setName> | <set#> ]"					},

	{ "select",	2,	2,	do_net_select,		7,	0,
		" select interface <interfaceName> | <interface#> | $child | $service\n"
		" select protocol  <protocolType>\n"
		" select service   <serviceName> | <service#>\n"
		" select set       <setName> | <set#>"						},

	{ "set",	2,	101,	do_net_set,		8,	0,
		" set interface context-sensitive-arguments (or ? for help)\n"
		" set protocol  context-sensitive-arguments (or ? for help)\n"
		" set service   [ name <serviceName> ] [ order new-order ]\n"
		" set set       [ name setName ]"						},

	{ "show",	1,	2,	do_net_show,		9,	0,
		" show interfaces\n"
		" show interface [ <interfaceName> | <interface#> ]\n"
		" show protocols\n"
		" show protocol  [ <protocolType> ]\n"
		" show services  [ all ]\n"
		" show service   [ <serviceName> | <service#> ]\n"
		" show sets\n\n"
		" show set       [ <setName> | <set#> ]"					},

	{ "snapshot",	0,	0,	do_net_snapshot,	99,	2,
		" snapshot"									}

};
__private_extern__
const int nCommands_prefs = (sizeof(commands_prefs)/sizeof(cmdInfo));


__private_extern__ cmdInfo	*commands		= NULL;
__private_extern__ int		nCommands		= 0;
__private_extern__ Boolean	enablePrivateAPI	= FALSE;
__private_extern__ Boolean	termRequested		= FALSE;


__private_extern__
void
do_command(int argc, char **argv)
{
	int	i;
	char	*cmd = argv[0];

	for (i = 0; i < nCommands; i++) {
		if ((commands[i].ctype > 1) && !enablePrivateAPI)  {
			continue;	/* if "private" API and access has not been enabled */
		}

		if (strcasecmp(cmd, commands[i].cmd) == 0) {
			--argc;
			argv++;
			if (argc < commands[i].minArgs) {
				SCPrint(TRUE, stdout, CFSTR("%s: too few arguments\n"), cmd);
				return;
			} else if (argc > commands[i].maxArgs) {
				SCPrint(TRUE, stdout, CFSTR("%s: too many arguments\n"), cmd);
				return;
			}
			(*commands[i].func)(argc, argv);
			return;
		}
	}

	SCPrint(TRUE, stdout, CFSTR("%s: unknown, type \"help\" for command info\n"), cmd);
	return;
}


__private_extern__
void
do_help(int argc, char **argv)
{
	int	g = -1;		/* current group */
	int	i;

	SCPrint(TRUE, stdout, CFSTR("\nAvailable commands:\n"));
	for (i = 0; i < nCommands; i++) {
		if (commands[i].ctype < 0)  {
			continue;	/* if "hidden" */
		}

		if ((commands[i].ctype > 0) && !enablePrivateAPI)  {
			continue;	/* if "private" API and access has not been enabled */
		}

		/* check if this is a new command group */
		if (g != commands[i].group) {
			SCPrint(TRUE, stdout, CFSTR("\n"));
			g = commands[i].group;
		}

		/* display the command */
		SCPrint(TRUE, stdout, CFSTR("%s\n"), commands[i].usage);
	}
	SCPrint(TRUE, stdout, CFSTR("\n"));

	return;
}


__private_extern__
void
do_readFile(int argc, char **argv)
{
	InputRef		src;

	/* allocate command input stream */
	src = (InputRef)CFAllocatorAllocate(NULL, sizeof(Input), 0);
	src->el = NULL;
	src->fp = fopen(argv[0], "r");

	if (src->fp == NULL) {
		SCPrint(TRUE, stdout, CFSTR("f.read: could not open file (%s).\n"), strerror(errno));
		CFAllocatorDeallocate(NULL, src);
		return;
	}

	/* open file, increase nesting level */
	SCPrint(TRUE, stdout, CFSTR("f.read: reading file (%s).\n"), argv[0]);
	nesting++;

	while (process_line(src) == TRUE) {
	       /* debug information, diagnostics */
		__showMachPortStatus();
	}

	(void)fclose(src->fp);
	CFAllocatorDeallocate(NULL, src);

	return;
}


__private_extern__
void
do_quit(int argc, char **argv)
{
	termRequested = TRUE;
	return;
}
