/* $Id: plserver.c,v 1.29 1994/08/25 04:00:27 mjl Exp $
 * $Log: plserver.c,v $
 * Revision 1.29  1994/08/25  04:00:27  mjl
 * Fixed some error output; elminates spurious <RET> at end.
 *
 * Revision 1.28  1994/07/19  22:31:43  mjl
 * All device drivers: enabling macro renamed to PLD_<driver>, where <driver>
 * is xwin, ps, etc.  See plDevs.h for more detail.  All internal header file
 * inclusion changed to /not/ use a search path so that it will work better
 * with makedepend.
 *
 * Revision 1.27  1994/06/30  18:45:02  mjl
 * Minor changes to pass gcc -Wall without warnings and other cleaning up.
 *
 * Revision 1.26  1994/06/23  22:33:53  mjl
 * Fixed bug introduced in last update concerning exit handling for DP style
 * communication.
 *
 * Revision 1.25  1994/06/16  19:04:32  mjl
 * Massively restructured.  Is now just a front-end to the pltkMain()
 * function.  Structured along the preferred lines for extended wish'es.
*/

/* 
 * plserver.c
 * Maurice LeBrun
 * 30-Apr-93
 *
 * Plplot graphics server.
 *
 * Just a front-end to the pltkMain() function.  Structured along the
 * preferred lines for extended wish'es.  Is typically run as a child
 * process from the plplot TK driver to render output.  Can use either TK
 * send or Tcl-DP RPC for communication, depending on how it is invoked.
 *
 * Note that plserver can be used the same way as wish or dpwish, as it
 * contains the functionality of each of these (except the -notk Tcl-DP
 * command-line option is not supported).  
 */
/*
#define DEBUG
*/

#include "plserver.h"

/* Application-specific command-line options */
/* Variable declarations */

static char *client_name;	/* Name of client main window */
static char *auto_path;		/* addition to auto_path */
static int child;		/* set if child of TK driver */
static int dp;			/* set if using Tcl-DP to communicate */
static char *client_host;	/* Host id for client */
static char *client_port;	/* Communications port id for client */

static Tk_ArgvInfo argTable[] = {
    {"-client_name", TK_ARGV_STRING, (char *) NULL, (char *) &client_name,
	 "Client main window name to connect to"},
    {"-client_host", TK_ARGV_STRING, (char *) NULL, (char *) &client_host,
	 "Client host to connect to"},
    {"-client_port", TK_ARGV_STRING, (char *) NULL, (char *) &client_port,
	 "Client port (Tcl-DP) to connect to"},
    {"-auto_path", TK_ARGV_STRING, (char *) NULL, (char *) &auto_path,
	 "Additional directory(s) to autoload"},
    {"-child", TK_ARGV_CONSTANT, (char *) 1, (char *) &child,
	 "Set ONLY when child of plplot TK driver"},
    {(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL,
	 (char *) NULL}
};

/* PLplot/Tk extension command -- handle exit. */

static int
plExitCmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);

/* Evals the specified command, aborting on an error. */

static void
tcl_cmd(Tcl_Interp *interp, char *cmd);

/*----------------------------------------------------------------------*\
 * main --
 *
 * Just a stub routine to call pltkMain.  The latter is nice to have
 * when building extended wishes, since then you don't have to rely on
 * sucking the Tk main out of libtk (which doesn't work correctly on all
 * systems/compilers/linkers/etc).  Hopefully in the future Tk will
 * supply a sufficiently capable tkMain() type function that can be used
 * instead. 
\*----------------------------------------------------------------------*/

int
main(int argc, char **argv)
{
    int i, myargc = argc;
    char *myargv[20];
    Tcl_Interp *interp;
    char *helpmsg = "Command-specific options:";

#ifdef DEBUG
    fprintf(stderr, "Program %s called with arguments :\n", argv[0]);
    for (i = 1; i < argc; i++) 
	fprintf(stderr, "%s ", argv[i]);
    fprintf(stderr, "\n");
#endif

/* Create interpreter just for argument parsing */

    interp = Tcl_CreateInterp();

/* Save arglist to get around tk_ParseArgv limitations */

    for (i = 0; i < argc; i++)
	myargv[i] = argv[i];

/* Parse args */
/* Examine the result string to see if an error return is really an error */

    if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv, argTable, 
		     TK_ARGV_NO_DEFAULTS) != TCL_OK) {
	fprintf(stderr, "\n(plserver) %s\n\n", interp->result);
	fprintf(stderr, "\
The client_<xxx> and -child options should not be used except via the\n\
Plplot/Tk driver.\n\n(wish) ");
	if (strncmp(interp->result, helpmsg, strlen(helpmsg)))
	    exit(1);
    }

/* No longer need interpreter */

    Tcl_DeleteInterp(interp);

/* Call pltkMain() with original argc/argv list, to make sure -h is seen */
/* Does not return until program exit */

    exit(pltkMain(myargc, myargv));
}


/*
 *----------------------------------------------------------------------
 *
 * Tcl_AppInit --
 *
 *	This procedure performs application-specific initialization.
 *	Most applications, especially those that incorporate additional
 *	packages, will have their own version of this procedure.
 *
 * Results:
 *	Returns a standard Tcl completion code, and leaves an error
 *	message in interp->result if an error occurs.
 *
 * Side effects:
 *	Depends on the startup script.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_AppInit(interp)
    Tcl_Interp *interp;		/* Interpreter for application. */
{
    Tk_Window main;

    main = Tk_MainWindow(interp);

    /*
     * Call the init procedures for included packages.  Each call should
     * look like this:
     *
     * if (Mod_Init(interp) == TCL_ERROR) {
     *     return TCL_ERROR;
     * }
     *
     * where "Mod" is the name of the module.
     */

    if (Tcl_Init(interp) == TCL_ERROR) {
	return TCL_ERROR;
    }
    if (main && Tk_Init(interp) == TCL_ERROR) {
	return TCL_ERROR;
    }
#ifdef PLD_dp
    if (Tdp_Init(interp) == TCL_ERROR) {
	return TCL_ERROR;
    }
#endif
    if (Pltk_Init(interp) == TCL_ERROR) {
	return TCL_ERROR;
    }

/* Application-specific startup.  That means: for use in plserver ONLY. */

/* Pass child variable to interpreter if set. */

    if (child != 0)
	Tcl_SetVar(interp, "child", "1", 0);

/* If client_name is set, TK send is being used to communicate. */
/* If client_port is set, Tcl-DP RPC is being used to communicate. */
/* The "dp" variable determines which style communication is used */

    if (client_name != NULL) {
	Tcl_SetVar(interp, "client_name", client_name, 0);
	dp = 0; tcl_cmd(interp, "set dp 0");
    }
    else if (client_port != NULL) {
#ifdef PLD_dp
	Tcl_SetVar(interp, "client_port", client_port, 0);
	if (client_host != NULL)
	    Tcl_SetVar(interp, "client_host", client_host, 0);
	dp = 1; tcl_cmd(interp, "set dp 1");
#else
	Tcl_AppendResult(interp,
			 "no Tcl-DP support in this version of plserver",
			 (char *) NULL);
	return TCL_ERROR;
#endif
    }

/* Add user-specified directory(s) to auto_path */

    if (auto_path != NULL) {
	Tcl_SetVar(interp, "dir", auto_path, 0);
	tcl_cmd(interp, "set auto_path \"$dir $auto_path\"");
    }

/* Rename "exit" to "tkexit", and insert custom exit handler */

    tcl_cmd(interp, "rename exit tkexit");

    Tcl_CreateCommand(interp, "exit", plExitCmd,
                      (ClientData) main, (void (*)(ClientData)) NULL);

    return TCL_OK;
}

/*----------------------------------------------------------------------*\
 * plExitCmd
 *
 * PLplot/Tk extension command -- handle exit.
 * The reason for overriding the normal exit command is so we can tell the
 * client to abort.
\*----------------------------------------------------------------------*/

static int
plExitCmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
{
    int value = 0;

/* Print error message if one given */

    if (interp->result != NULL && interp->result[0] != '\0')
	fprintf(stderr, "%s\n", interp->result);

/* Best to check the syntax before proceeding */

    if ((argc != 1) && (argc != 2)) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			 " ?returnCode?\"", (char *) NULL);
	return TCL_ERROR;
    }
    if ((argc != 1) && (Tcl_GetInt(interp, argv[1], &value) != TCL_OK)) {
	Tcl_AppendResult(interp, "non-integer return code: \"", argv[1],
			 "\"", (char *) NULL);
	return TCL_ERROR;
    }

/* If client exists, tell it to self destruct */

    if (client_name != NULL) {
	client_name = NULL;
	Tcl_VarEval(interp, "send $client after 1 abort", (char **) NULL);
    }
    else if (client_port != NULL) {
	client_port = NULL;
	Tcl_VarEval(interp, "dp_RDO $client abort", (char **) NULL);
    }

/* Now really exit */

    return Tcl_VarEval(interp, "tkexit", argv[1], (char **) NULL);
}

/*----------------------------------------------------------------------*\
* tcl_cmd
*
* Evals the specified command, aborting on an error.
\*----------------------------------------------------------------------*/

static void
tcl_cmd(Tcl_Interp *interp, char *cmd)
{
    int result;

    dbug_enter("tcl_cmd");
#ifdef DEBUG_ENTER
    fprintf(stderr, "plserver: evaluating command %s\n", cmd);
#endif

    result = Tcl_VarEval(interp, cmd, (char **) NULL);
    if (result != TCL_OK) {
	Tcl_Eval(interp, "exit");
	exit(1);
    }
}
