/* -*-C-*-
 * gnuserv.c    -- pbreton Thu Jan 25 1996
 *
 * Server code for handling requests from clients and forwarding them
 * on to the GNU Emacs process.
 *
 * This file is not part of GNU Emacs. 
 * 
 * Copying is permitted under those conditions described by the GNU
 * General Public License.
 *
 * Copyright (C) 1996 Peter Breton/Nico Francois
 *
 * Author: Peter Breton (pbreton@cs.umb.edu)
 * Based heavily on Andy Norman's gnuserv (ange@hplb.hpl.hp.com), 
 * which in turn was based on 'etc/emacsclient.c' from the GNU 
 * Emacs 18.52 distribution.
 *
 * Modifications: Nico Francois (nico.francois@scala.nl) 
 *                Mon Jun 3 1996
 * Significant rework to use mailslots instead of named pipes.
 * Following improvements have been made:
 *   - now works on Windows 95 as well as NT.
 *   - uses a thread to handle client requests while the main process
 *     reads the results from Emacs.
 *   - results from Emacs are sent back to the appropriate client.
 *     NOTE: currently still limited to clients that run on the same
 *           machine as gnuserv!
 *   - can be aborted so the task will not hang around on Win95 (not sure
 *     if this was a problem on NT).
 *
 *                Sat Jun 8 1996
 * Moved reading from stdin to thread and waiting on mailslot to main
 * process.
 * Fixed problem with client mailslotname on Windows NT.
 * Sending result to mailslot will now open the mailslot only when needed.
 * This works around a problem with a sharing violation error on NT.
 *
 *                Thu Jun 27 1996
 * Cleaned up the code a bit and added more/better comments.
 * Sharing violation error on NT now properly handled in gnuslib.c.
 * Made gnuslib.c check for NT/95 and handle things differently when
 * running on Windows 95 (to avoid silly 100% CPU time usage on 95).
 *
 * $Log$ */

static char rcsid[] = "$Header$";

#include "gnuserv.h" 

static char *progname = NULL;

static char message[GSERV_BUFSZ + 1];

enum {
	MSGTYPE_UNKNOWN,
	MSGTYPE_REQUEST,        // Client will start request strings with "C:"
	MSGTYPE_RESULT,         // Result string starts with "R:"
};

void HandleClientRequests (HANDLE mailslot)
{
	while (1) {
		int slotdone = FALSE;

		while (!slotdone) {
			DWORD clientid;
			DWORD bytesread;
			int msgtype;

			// Read the message and check to see if read was successful
			msgtype = MSGTYPE_UNKNOWN;
			while (ReadFile (mailslot, message, sizeof (message),
					&bytesread, (LPOVERLAPPED)NULL)) {
				// If we actually received anything, handle what we got.
				if (bytesread) {
					char *msg = message;

					message[bytesread] = '\0';

					// For starters we'll need to find the message type
					if (msgtype == MSGTYPE_UNKNOWN) {
						if (!strncmp ("R:", message, 2)) {
							msgtype = MSGTYPE_RESULT;
							msg += 2;
							}
						else if (!strncmp ("C:", message, 2)) {
							msgtype = MSGTYPE_REQUEST;
							msg += 2;
							clientid = atol (msg);
							while (*msg && *msg++ != ' ');
							printf ("%u ", clientid);
							}
						}

					if (msgtype == MSGTYPE_REQUEST) {
						if (*msg) {
							printf ("%s", msg);
							}
						if (strchr (msg, EOT_CHR)) {
							fflush (stdout);
							slotdone = TRUE;
							break;
							}
						}
					else if (msgtype == MSGTYPE_RESULT) {
						// Send result back to client
						if (*msg) {
							HANDLE clientslot;

							// Get client-id from result string, then
							// skip until after ':' so we only send
							// the actual result string back.
							clientid = atol (msg);
							while (*msg && *msg++ != ':');
							// Client will be waiting for the result
							// on a mailslot with the following name:
							//   \\hostname\mailslot\emacs\gnuclient\ABADCAFE
							// With ABADCAFE being the clientid in hex (8 chars).
							// FIX! hostname is always . right now!
							clientslot = ConnectToMailslot (GNUSERV_DEFAULT_HOST,
									TRUE, clientid);
							if (clientslot != INVALID_HANDLE_VALUE) {
								// Don't use SendString() since we don't want
								// to know about any errors.
								DWORD byteswritten;
								WriteFile (clientslot, msg, strlen(msg) + 1,
										&byteswritten, (LPOVERLAPPED)NULL);
								DisconnectFromMailslot (clientslot);
								}
							}
						// Result message is terminated by a newline
						if (strchr (msg, '\n')) {
							slotdone = TRUE;
							break;
							}
						}
					}
				else {
					// We should only get here on Windows 95 (mailslot
					// timeout set to 0).
					// We did not receive anything.  Sleep for 2/10th
					// of a second, then try again.
					Sleep (200);
					}
				}
			}
		}
}

// This function will run as a separate thread.
// It will read results from stdin (sent by Emacs) and will send them
// to the mailslot that is polled in our main loop.
DWORD HandleEmacsResults (LPDWORD lpdwParam)
{
	char inputbuff[256];
	DWORD bytesread;
	HANDLE sendslot, in;

	// Get handle to stdin
	in = GetStdHandle (STD_INPUT_HANDLE);

	while (1) {
		while (ReadFile (in, inputbuff, sizeof (inputbuff),
				&bytesread, (LPOVERLAPPED)NULL)) {
			inputbuff[bytesread] = '\0';
			// Send input (result) to our thread, so it can be sent
			// back to the client.
			sendslot = ConnectToMailslot (GNUSERV_DEFAULT_HOST, FALSE, 0);
			if (sendslot != INVALID_HANDLE_VALUE) {
				SendString (sendslot, "R:");
				SendString (sendslot, inputbuff);
				DisconnectFromMailslot (sendslot);
				}
			}
		}

	return (0);
}

void main (int argc, char *argv[])
{
	HANDLE mailslot;
	DWORD threadid;
	HANDLE thread;

	progname = argv[0];

	// Create mailslot.
	mailslot = CreateGnuServSlot (FALSE, 0);
	if (mailslot == INVALID_HANDLE_VALUE)
		exit (1);

	// Create thread to read Emacs results from stdin and send them
	// to our mailslot.
	thread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL, 0,
			(LPTHREAD_START_ROUTINE)HandleEmacsResults,
			NULL, 0, &threadid);

	// Handle all client requests (and results)
	HandleClientRequests (mailslot);

	// Cleanup, then exit
	DisconnectFromMailslot (mailslot);
}
