/* -*-C-*-
 * Common library code for the GNU Emacs server and client.
 *
 * This file is not part of GNU Emacs. 
 * 
 * Copying is permitted under those conditions described by the GNU
 * General Public License.
 *
 * Copyright (C) 1996 Nico Francois
 *
 * Author: Nico Francois
 * Based heavily on Peter Breton's code (pbreton@cs.umb.edu), which itself
 * was 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 Sep 16 1996
 * If WIN_VERSION is defined error messages are put up using the Win32
 * MessageBox() function.
 * If gnuclient or gnudoit can't open the gnuserv mailslot we now try
 * running 'runemacs.exe' and then try again (for a maximum of 30 seconds).
 */

#include "gnuserv.h"

HANDLE CreateGnuServSlot (BOOL clientslot, DWORD clientid)
{
	char buff[sizeof (GNUSERV_RECEIVE_CLIENTSLOT) + 10];
	char *slotname = GNUSERV_RECEIVE_MAILSLOT;
	HANDLE slot;

	// Do we need to create a slot for the server or client ?
	if (clientslot) {
		slotname = buff;
		sprintf (slotname, GNUSERV_RECEIVE_CLIENTSLOT, clientid);
		}

	// Create a mailslot for receiving messages
	// If we are running on Windows 95 we need to set the time-out to
	// 0 seconds to work around two problems: we will be able to get
	// aborted properly when Emacs quits and we will not consume 100%
	// CPU time.
	// Note that our "receive code" will loop when reading the mailslot
	// and sleep for a small amount of time when nothing was received.
	// On Windows NT all this rubbish is not necessary and we will simply
	// wait forever.
	slot = CreateMailslot (slotname, 0,
			IsWindowsNT() ? MAILSLOT_WAIT_FOREVER : 0, NULL);

	// Check and see if the mailslot was created
	if (INVALID_HANDLE_VALUE == slot)
		OutputErrorString ("Unable to create mailslot");

	return (slot);
}

BOOL StartEmacs (void)
{
	PROCESS_INFORMATION procinfo;
	STARTUPINFO startupinfo;

	memset (&startupinfo, 0, sizeof (STARTUPINFO));
	startupinfo.cb = sizeof (STARTUPINFO);
	startupinfo.wShowWindow = SW_SHOW;

	if (!CreateProcess (NULL, "runemacs", NULL, NULL, FALSE,
			CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
			&startupinfo, &procinfo)) {
		OutputErrorString ("Could not start runemacs.exe");
		return (FALSE);
		}
	return (TRUE);
}

HANDLE ConnectToMailslot (char *host, BOOL clientslot, DWORD clientid)
{
	char slotname[sizeof (GNUSERV_SEND_MAILSLOT) + 80];
	HANDLE slot;
	int runemacs = 0;

	if (clientslot)
		sprintf (slotname, GNUSERV_SEND_CLIENTSLOT, host, clientid);
	else
		sprintf (slotname, GNUSERV_SEND_MAILSLOT, host);

	while (1) {
		// Create the mailslot file handle
		slot = CreateFile (slotname, GENERIC_WRITE, FILE_SHARE_READ,
				(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);

		// Check and see if the mailslot file was opened correctly
		if (INVALID_HANDLE_VALUE != slot) break;

		// Check if last error was a sharing violation error.
		if (GetLastError() == ERROR_SHARING_VIOLATION) {
			// A sharing violation occured.  This means someone else
			// is in the process of writing to the mailslot.  We'll
			// simply wait a bit and then try again.  We'll be able to
			// get a handle as soon as the other process does a
			// CloseHandle().
			Sleep (250);
			}
		else {
			if (!clientslot && (ERROR_FILE_NOT_FOUND == GetLastError())) {
				if (runemacs == 0) {
					// Since we could not connect to the mailslot we
					// are going to assume this failed since gnuserv was
					// not running.  We'll try to start Emacs by
					// running runemacs.exe and then try again.
					if (!StartEmacs())
						break;
					}
				// Don't wait longer than 30 seconds.  If Emacs hasn't
				// launched the server by then we give up.
				// Even succesfully launching Emacs doesn't guarantee it
				// starting gnuserv, so we have to give up at one time.
				if (++runemacs < 30) {
					// Try to connect again every second.
					Sleep (1000);
					continue;
					}
				}
			OutputErrorString ("Unable to connect to mailslot");
			if (!clientslot && (ERROR_FILE_NOT_FOUND == GetLastError()))
				fprintf (stderr,
						"Make sure Emacs/gnuserv program is running.\n");
			break;
			}
		}

	return (slot);
}

void DisconnectFromMailslot (HANDLE slot)
{
	CloseHandle (slot);
}

// Send string to mailslot, returns boolean indicating success or failure.
BOOL SendString (HANDLE slot, char *msg)
{
	DWORD byteswritten;
	int runemacs = 0;

	while (1) {
		// Write message to the mailslot
		if (!WriteFile (slot, msg, (DWORD)strlen(msg) + 1,
				&byteswritten, (LPOVERLAPPED)NULL)) {
			if (ERROR_FILE_NOT_FOUND == GetLastError()) {
				if (runemacs == 0) {
					// Since we could not write to the mailslot we are
					// going to assume this failed since gnuserv was not
					// running.  We'll try to start Emacs by running
					// runemacs.exe and then try again.
					if (!StartEmacs())
						return (FALSE);
					}
				// Don't wait longer than 30 seconds.  If Emacs hasn't
				// launched the server by then we give up.
				// Even succesfully launching Emacs doesn't guarantee it
				// starting gnuserv, so we have to give up at one time.
				if (++runemacs < 30) {
					// Try to write again every second.
					Sleep (1000);
					continue;
					}
				OutputErrorString ("Unable to write to mailslot");
				fprintf(stderr, "Make sure Emacs/gnuserv program is running.\n");
				}
			else {
				OutputErrorString ("Unable to write to mailslot");
				}
			return (FALSE);
			}
		return (TRUE);
		}
}

void WaitForServerResult (HANDLE slot, BOOL echo)
{
	char result[256];
	DWORD bytesread;
	int waiting = TRUE;

	while (waiting) {
		while (ReadFile (slot, result, sizeof (result), &bytesread, NULL)) {
			if (bytesread) {
				result[bytesread] = '\0';
				if (echo)
					printf ("%s", result);
				if (strchr (result, '\n')) {
					waiting = FALSE;
					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);
				}
			}
		}
}

BOOL IsWindowsNT (void)
{
	return (GetVersion() < 0x80000000);
}

void OutputErrorString (char *errormsg)
{
#ifdef WIN_VERSION
	char message[256];
	sprintf (message, "%s. Error code: %d\n", errormsg, GetLastError());
	MessageBox (NULL, message, NULL, MB_ICONEXCLAMATION);
#else
	fprintf (stderr, "%s. Error code: %d\n", errormsg, GetLastError());
	fflush (stderr);
#endif
}
