Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / win / pgnuplot.c
diff --git a/src/win/pgnuplot.c b/src/win/pgnuplot.c
new file mode 100644 (file)
index 0000000..984e89c
--- /dev/null
@@ -0,0 +1,355 @@
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: pgnuplot.c,v 1.15.2.2 2008/10/10 21:17:29 mikulik Exp $"); }
+#endif
+
+/*
+ * pgnuplot.c -- pipe stdin to wgnuplot
+ *
+ * Version 0.4 -- October 2002
+ *
+ * This program is based on pgnuplot.c Copyright (C) 1999 Hans-Bernhard Broeker
+ * with substantial modifications Copyright (C) 1999 Craig R. Schardt.
+ *
+ * The code is released to the public domain.
+ *
+ */
+
+/* Changes by Petr Mikulik, October 2002:
+ * Added command line options --version and --help, and consequently dependency
+ * on gnuplot's version.h and version.c.
+ * Compile pgnuplot by:
+ *     gcc -O2 -s -o pgnuplot.exe pgnuplot.c ../version.c -I.. -luser32
+ */
+
+/* Comments from original pgnuplot.c */
+/*
+ * pgnuplot.c -- 'Pipe to gnuplot'  Version 990608
+ *
+ * A small program, to be compiled as a Win32 console mode application.
+ * (NB: will not work on 16bit Windows, not even with Win32s installed).
+ *
+ * This program will accept commands on STDIN, and pipe them to an
+ * active (or newly created) wgnuplot text window. Command line options
+ * are passed on to wgnuplot.
+ *
+ * Effectively, this means `pgnuplot' is an almost complete substitute
+ * for `wgnuplot', on the command line, with the added benefit that it
+ * does accept commands from redirected stdin. (Being a Windows GUI
+ * application, `wgnuplot' itself cannot read stdin at all.)
+ *
+ * Copyright (C) 1999 by Hans-Bernhard Broeker
+ *                       (broeker@physik.rwth-aachen.de)
+ * This file is in the public domain. It might become part of a future
+ * distribution of gnuplot.
+ *
+ * based on a program posted to comp.graphics.apps.gnuplot in May 1998 by
+ * jl Hamel <jlhamel@cea.fr>
+ *
+ * Changes relative to that original version:
+ * -- doesn't start a new wgnuplot if one already is running.
+ * -- doesn't end up in an endless loop if STDIN is not redirected.
+ *    (refuses to read from STDIN at all, in that case).
+ * -- doesn't stop the wgnuplot session at the end of
+ *    stdin, if it didn't start wgnuplot itself.
+ * -- avoids the usual buffer overrun problem with gets().
+ *
+ * For the record, I usually use MinGW32 to compile this, with a
+ * command line looking like this:
+ *
+ *     gcc -o pgnuplot.exe pgnuplot.c -luser32 -s
+ *
+ * Note that if you're using Cygwin GCC, you'll want to add the option
+ * -mno-cygwin to that command line to avoid getting a pgnuplot.exe
+ * that depends on their GPL'ed cygwin1.dll.
+ */
+
+/*     Modifications by Craig R. Schardt (17 Jun 1999)
+
+       Copyright (C) 1999 by Craig R. Schardt (craig@silica.mse.ufl.edu)
+
+       Major changes: (See the explanation below for more information)
+               + Always starts a new instance of wgnuplot.
+               + If stdin isn't redirected then start wgnuplot and give it focus.
+               + Uses CreateProcess() instead of WinExec() to start wgnuplot when stdin
+                 is redirected.
+
+       Other changes:
+               + New technique for building the command line to pass to wgnuplot.exe
+                 which is less complicated and seems to work more reliably than the old
+                 technique.
+               + Simplified message passing section of the code.
+               + All printf(...) statements are now fprintf(stderr,...) so that errors
+                 are sent to the console, even if stdout is redirected.
+
+       The previous version of pgnuplot would fail when more than one program
+       tried to access wgnuplot simultaneously or when one program tried to start
+       more than one wgnuplot session. Only a single instance of wgnuplot would be
+       started and all input would be sent to that instance. When two or more programs
+       tried to pipe input to wgnuplot, the two seperate input streams would be sent
+       to one wgnuplot window resulting in one very confused copy of wgnuplot. The only
+       way to avoid this problem was to change pgnuplot so that it would start a
+       new instance of wgnuplot every time.
+
+       Just starting a new instance of wgnuplot isn't enough. pgnuplot must also
+       make sure that the data on each stdin pipe is sent to the proper wgnuplot
+       instance. This is achieved by using CreateProcess() which returns a handle
+       to the newly created process. Once the process has initialized, it can be
+       searched for the text window and then data can be routed correctly. The search
+       is carried out by the EnumThreadWindows() call and the data passing is carried
+       out by a rewritten version of the original code. With these changes, pgnuplot
+       now behaves in a manner consistent with the behavior of gnuplot on UNIX
+       computers.
+
+       This program has been compiled using Microsoft Visual C++ 4.0 with the
+       following command line:
+
+               cl /O2 pgnuplot.c /link user32.lib
+
+       The resulting program has been tested on WinNT and Win98 both by calling
+       it directly from the command line with and without redirected input. The
+       program also works on WinNT with a modified version of Gnuplot.py (a script
+       for interactive control of Gnuplot from Python).
+
+       22 JUN 1999:
+       + Fixed command line code to behave properly when the first
+         item is quoted in the original command line.
+
+       29 JUN 1999:
+       + Added some code to print the command line. This is for testing
+         only and should be removed before the general release. To enable,
+         compile with SHOWCMDLINE defined.
+
+       30 JUN 1999:
+       + New function FindUnquotedSpace() which replaces the earlier technique for
+         finding the command line arguments to send on to wgnuplot. Prior to this
+         the arguments were assumed to start after argv[0], however argv[0] is not
+         set the same by all combinitaions of compiler, command processor, and OS.
+         The new method ignores argv completely and manually search the command line
+         string for the first space which isn't enclosed in double-quotes.
+
+  */
+
+#include <io.h>
+#include <conio.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <windows.h>
+#include "version.h"
+
+#ifndef _O_BINARY
+# define _O_BINARY O_BINARY
+#endif
+#if (__BORLANDC__ >= 0x450) /* about BCBuilder 1.0 */
+# define _setmode setmode
+#endif
+#ifdef __WATCOMC__
+# define _setmode setmode
+#endif
+
+/* Customize this path if needed */
+#define PROGNAME "wgnuplot.exe"
+/* CRS: The value given above will work correctly as long as pgnuplot.exe
+ * is in the same directory as wgnuplot.exe or the directory containing
+ * wgnuplot.exe is included in the path. I would recommend placing the
+ * pgnuplot.exe executable in the same directory as wgnuplot.exe and
+ * leaving this definition alone.
+ */
+
+#define WINDOWNAME "gnuplot"
+#define PARENTCLASS "wgnuplot_parent"
+#define TEXTCLASS "wgnuplot_text"
+#define GRAPHWINDOW "gnuplot graph"
+#define GRAPHCLASS "wgnuplot_graph"
+#define BUFFER_SIZE 80
+
+/* GLOBAL Variables */
+HWND hwndParent = NULL;
+HWND hwndText = NULL;
+
+PROCESS_INFORMATION piProcInfo;
+STARTUPINFO         siStartInfo;
+
+/* CRS: Callback for the EnumThreadWindows function */
+BOOL CALLBACK
+cbGetTextWindow(HWND  hwnd, LPARAM  lParam)
+{
+    /* save the value of the parent window */
+    hwndParent = hwnd;
+    /* check to see if it has a child text window */
+    hwndText = FindWindowEx(hwnd, NULL, TEXTCLASS, NULL);
+
+    /* if the text window was found, stop looking */
+    return (hwndText == NULL);
+}
+
+/* sends a string to the specified window */
+/* CRS: made this into a function call */
+void
+PostString(HWND hwnd, char *pc)
+{
+    while(*pc) {
+       PostMessage(hwnd, WM_CHAR, (unsigned char) *pc, 1L);
+       /* CRS: should add a check of return code on PostMessage. If 0, the
+          message que was full and the message wasn't posted. */
+       pc++;
+    }
+}
+
+/* FindUnquotedSpace(): Search a string for the first space not enclosed in quotes.
+ *   Returns a pointer to the space, or the empty string if no space is found.
+ *   -CRS 30061999
+ */
+char*
+FindUnquotedSpace(char *pc)
+{
+    while ((*pc) && (*pc != ' ') && (*pc != '\t')) {
+       if (*pc == '"') {
+           do {
+               pc++;
+           } while (pc[1] && (*pc != '"'));
+       }
+       pc++;
+    }
+    return pc;
+}
+
+int
+main (int argc, char *argv[])
+{
+    char    psBuffer[BUFFER_SIZE];
+    char    psGnuplotCommandLine[MAX_PATH] = PROGNAME;
+    LPTSTR  psCmdLine;
+    BOOL    bSuccess;
+    BOOL    bPersist = FALSE;
+    int        i;
+
+#if !defined(_O_BINARY) && defined(O_BINARY)
+# define _O_BINARY O_BINARY
+# define _setmode setmode /* this is for BC4.5 ... */
+#endif
+    _setmode(fileno(stdout), _O_BINARY);
+
+    for (i = 1; i < argc; i++) {
+       if (!argv[i])
+           continue;
+       if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) {
+           printf("gnuplot %s patchlevel %s\n",
+                  gnuplot_version, gnuplot_patchlevel);
+           return 0;
+       } else if ((!stricmp(argv[i], "-noend")) || (!stricmp(argv[i], "/noend")) || 
+                  (!stricmp(argv[i], "-persist"))) {
+           bPersist = TRUE;
+       } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+           printf("Usage: gnuplot [OPTION] [FILE] [-]\n"
+                   "  -V, --version       show gnuplot version\n"
+                   "  -h, --help          show this help\n"
+                   "  -e \"cmd; cmd; ...\"  prepand additional commands\n"
+                   "  -persist            don't close the plot after executing FILE\n"
+                   "  -noend, /noend      like -persist (non-portable Windows-only options)\n"
+                   "  -                   allow work in interactive mode after executing FILE\n"
+                   "Only on Windows, -persist and - have the same effect.\n"
+                   "This is gnuplot %s patchlevel %s\n"
+                   "Report bugs to <http://sourceforge.net/projects/gnuplot>\n",
+                   gnuplot_version, gnuplot_patchlevel);
+           return 0;
+       }
+    } /* for(argc) */
+
+    /* CRS: create the new command line, passing all of the command
+     * line options to wgnuplot so that it can process them:
+     * first, get the command line,
+     * then move past the name of the program (e.g., 'pgnuplot'),
+     * finally, add what's left of the line onto the gnuplot command line. */
+    psCmdLine = GetCommandLine();
+
+#ifdef SHOWCMDLINE
+    fprintf(stderr,"CmdLine: %s\n", psCmdLine);
+    fprintf(stderr,"argv[0]: %s\n",argv[0]);
+#endif
+
+    /* CRS 30061999: Search for the first unquoted space. This should
+       separate the program name from the arguments. */
+    psCmdLine = FindUnquotedSpace(psCmdLine);
+
+    strncat(psGnuplotCommandLine, psCmdLine, MAX_PATH - strlen(psGnuplotCommandLine));
+
+#ifdef SHOWCMDLINE
+    fprintf(stderr,"Arguments: %s\n", psCmdLine);
+    fprintf(stderr,"GnuplotCommandLine: %s\n",psGnuplotCommandLine);
+#endif
+
+    /* CRS: if stdin isn't redirected then just launch wgnuplot normally
+     * and exit. */
+    if (isatty(fileno(stdin))) {
+       if (WinExec(psGnuplotCommandLine, SW_SHOWDEFAULT) > 31) {
+           exit(EXIT_SUCCESS);
+       }
+       fprintf(stderr,"ERROR %u: Couldn't execute: \"%s\"\n",
+               GetLastError(), psGnuplotCommandLine);
+       exit(EXIT_FAILURE);
+    }
+
+    /* CRS: initialize the STARTUPINFO and call CreateProcess(). */
+    siStartInfo.cb = sizeof(STARTUPINFO);
+    siStartInfo.lpReserved = NULL;
+    siStartInfo.lpReserved2 = NULL;
+    siStartInfo.cbReserved2 = 0;
+    siStartInfo.lpDesktop = NULL;
+    siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
+    siStartInfo.wShowWindow = SW_SHOWMINIMIZED;
+
+    bSuccess = CreateProcess(
+                            NULL,                   /* pointer to name of executable module   */
+                            psGnuplotCommandLine,   /* pointer to command line string         */
+                            NULL,                   /* pointer to process security attributes */
+                            NULL,                   /* pointer to thread security attributes  */
+                            FALSE,                  /* handle inheritance flag                */
+                            0,                      /* creation flags                         */
+                            NULL,                   /* pointer to new environment block       */
+                            NULL,                   /* pointer to current directory name      */
+                            &siStartInfo,           /* pointer to STARTUPINFO                 */
+                            &piProcInfo             /* pointer to PROCESS_INFORMATION         */
+                            );
+
+    /* if CreateProcess() failed, print a warning and exit. */
+    if (! bSuccess) {
+       fprintf(stderr,"ERROR %u: Couldn't execute: \"%s\"\n",
+               GetLastError(), psGnuplotCommandLine);
+       exit(EXIT_FAILURE);
+    }
+
+    /* CRS: give gnuplot enough time to start (1 sec.) */
+    if (WaitForInputIdle(piProcInfo.hProcess, 1000)) {
+       fprintf(stderr, "Timeout: gnuplot is not ready\n");
+       exit(EXIT_FAILURE);
+    }
+
+    /* CRS: get the HWND of the parent window and text windows */
+    EnumThreadWindows(piProcInfo.dwThreadId, cbGetTextWindow, 0);
+
+    /* CRS: free the process and thread handles */
+    CloseHandle(piProcInfo.hProcess);
+    CloseHandle(piProcInfo.hThread);
+
+    if (! hwndParent || ! hwndText) {
+       /* Still no gnuplot window? Problem! */
+       fprintf(stderr, "Can't find the gnuplot window");
+       exit(EXIT_FAILURE);
+    }
+
+    /* wait for commands on stdin, and pass them on to the wgnuplot text
+     * window */
+    while (fgets(psBuffer, BUFFER_SIZE, stdin) != NULL) {
+       PostString(hwndText, psBuffer);
+    }
+
+    /* exit gracefully, unless -persist is requested */
+    if (!bPersist) {
+       /* CRS: Add a test to see if gnuplot is still running? */
+       PostString(hwndText, "\nexit\n");
+    }
+
+    return EXIT_SUCCESS;
+}