2 static char *RCSid() { return RCSid("$Id: pgnuplot.c,v 1.15.2.2 2008/10/10 21:17:29 mikulik Exp $"); }
6 * pgnuplot.c -- pipe stdin to wgnuplot
8 * Version 0.4 -- October 2002
10 * This program is based on pgnuplot.c Copyright (C) 1999 Hans-Bernhard Broeker
11 * with substantial modifications Copyright (C) 1999 Craig R. Schardt.
13 * The code is released to the public domain.
17 /* Changes by Petr Mikulik, October 2002:
18 * Added command line options --version and --help, and consequently dependency
19 * on gnuplot's version.h and version.c.
20 * Compile pgnuplot by:
21 * gcc -O2 -s -o pgnuplot.exe pgnuplot.c ../version.c -I.. -luser32
24 /* Comments from original pgnuplot.c */
26 * pgnuplot.c -- 'Pipe to gnuplot' Version 990608
28 * A small program, to be compiled as a Win32 console mode application.
29 * (NB: will not work on 16bit Windows, not even with Win32s installed).
31 * This program will accept commands on STDIN, and pipe them to an
32 * active (or newly created) wgnuplot text window. Command line options
33 * are passed on to wgnuplot.
35 * Effectively, this means `pgnuplot' is an almost complete substitute
36 * for `wgnuplot', on the command line, with the added benefit that it
37 * does accept commands from redirected stdin. (Being a Windows GUI
38 * application, `wgnuplot' itself cannot read stdin at all.)
40 * Copyright (C) 1999 by Hans-Bernhard Broeker
41 * (broeker@physik.rwth-aachen.de)
42 * This file is in the public domain. It might become part of a future
43 * distribution of gnuplot.
45 * based on a program posted to comp.graphics.apps.gnuplot in May 1998 by
46 * jl Hamel <jlhamel@cea.fr>
48 * Changes relative to that original version:
49 * -- doesn't start a new wgnuplot if one already is running.
50 * -- doesn't end up in an endless loop if STDIN is not redirected.
51 * (refuses to read from STDIN at all, in that case).
52 * -- doesn't stop the wgnuplot session at the end of
53 * stdin, if it didn't start wgnuplot itself.
54 * -- avoids the usual buffer overrun problem with gets().
56 * For the record, I usually use MinGW32 to compile this, with a
57 * command line looking like this:
59 * gcc -o pgnuplot.exe pgnuplot.c -luser32 -s
61 * Note that if you're using Cygwin GCC, you'll want to add the option
62 * -mno-cygwin to that command line to avoid getting a pgnuplot.exe
63 * that depends on their GPL'ed cygwin1.dll.
66 /* Modifications by Craig R. Schardt (17 Jun 1999)
68 Copyright (C) 1999 by Craig R. Schardt (craig@silica.mse.ufl.edu)
70 Major changes: (See the explanation below for more information)
71 + Always starts a new instance of wgnuplot.
72 + If stdin isn't redirected then start wgnuplot and give it focus.
73 + Uses CreateProcess() instead of WinExec() to start wgnuplot when stdin
77 + New technique for building the command line to pass to wgnuplot.exe
78 which is less complicated and seems to work more reliably than the old
80 + Simplified message passing section of the code.
81 + All printf(...) statements are now fprintf(stderr,...) so that errors
82 are sent to the console, even if stdout is redirected.
84 The previous version of pgnuplot would fail when more than one program
85 tried to access wgnuplot simultaneously or when one program tried to start
86 more than one wgnuplot session. Only a single instance of wgnuplot would be
87 started and all input would be sent to that instance. When two or more programs
88 tried to pipe input to wgnuplot, the two seperate input streams would be sent
89 to one wgnuplot window resulting in one very confused copy of wgnuplot. The only
90 way to avoid this problem was to change pgnuplot so that it would start a
91 new instance of wgnuplot every time.
93 Just starting a new instance of wgnuplot isn't enough. pgnuplot must also
94 make sure that the data on each stdin pipe is sent to the proper wgnuplot
95 instance. This is achieved by using CreateProcess() which returns a handle
96 to the newly created process. Once the process has initialized, it can be
97 searched for the text window and then data can be routed correctly. The search
98 is carried out by the EnumThreadWindows() call and the data passing is carried
99 out by a rewritten version of the original code. With these changes, pgnuplot
100 now behaves in a manner consistent with the behavior of gnuplot on UNIX
103 This program has been compiled using Microsoft Visual C++ 4.0 with the
104 following command line:
106 cl /O2 pgnuplot.c /link user32.lib
108 The resulting program has been tested on WinNT and Win98 both by calling
109 it directly from the command line with and without redirected input. The
110 program also works on WinNT with a modified version of Gnuplot.py (a script
111 for interactive control of Gnuplot from Python).
114 + Fixed command line code to behave properly when the first
115 item is quoted in the original command line.
118 + Added some code to print the command line. This is for testing
119 only and should be removed before the general release. To enable,
120 compile with SHOWCMDLINE defined.
123 + New function FindUnquotedSpace() which replaces the earlier technique for
124 finding the command line arguments to send on to wgnuplot. Prior to this
125 the arguments were assumed to start after argv[0], however argv[0] is not
126 set the same by all combinitaions of compiler, command processor, and OS.
127 The new method ignores argv completely and manually search the command line
128 string for the first space which isn't enclosed in double-quotes.
142 # define _O_BINARY O_BINARY
144 #if (__BORLANDC__ >= 0x450) /* about BCBuilder 1.0 */
145 # define _setmode setmode
148 # define _setmode setmode
151 /* Customize this path if needed */
152 #define PROGNAME "wgnuplot.exe"
153 /* CRS: The value given above will work correctly as long as pgnuplot.exe
154 * is in the same directory as wgnuplot.exe or the directory containing
155 * wgnuplot.exe is included in the path. I would recommend placing the
156 * pgnuplot.exe executable in the same directory as wgnuplot.exe and
157 * leaving this definition alone.
160 #define WINDOWNAME "gnuplot"
161 #define PARENTCLASS "wgnuplot_parent"
162 #define TEXTCLASS "wgnuplot_text"
163 #define GRAPHWINDOW "gnuplot graph"
164 #define GRAPHCLASS "wgnuplot_graph"
165 #define BUFFER_SIZE 80
167 /* GLOBAL Variables */
168 HWND hwndParent = NULL;
169 HWND hwndText = NULL;
171 PROCESS_INFORMATION piProcInfo;
172 STARTUPINFO siStartInfo;
174 /* CRS: Callback for the EnumThreadWindows function */
176 cbGetTextWindow(HWND hwnd, LPARAM lParam)
178 /* save the value of the parent window */
180 /* check to see if it has a child text window */
181 hwndText = FindWindowEx(hwnd, NULL, TEXTCLASS, NULL);
183 /* if the text window was found, stop looking */
184 return (hwndText == NULL);
187 /* sends a string to the specified window */
188 /* CRS: made this into a function call */
190 PostString(HWND hwnd, char *pc)
193 PostMessage(hwnd, WM_CHAR, (unsigned char) *pc, 1L);
194 /* CRS: should add a check of return code on PostMessage. If 0, the
195 message que was full and the message wasn't posted. */
200 /* FindUnquotedSpace(): Search a string for the first space not enclosed in quotes.
201 * Returns a pointer to the space, or the empty string if no space is found.
205 FindUnquotedSpace(char *pc)
207 while ((*pc) && (*pc != ' ') && (*pc != '\t')) {
211 } while (pc[1] && (*pc != '"'));
219 main (int argc, char *argv[])
221 char psBuffer[BUFFER_SIZE];
222 char psGnuplotCommandLine[MAX_PATH] = PROGNAME;
225 BOOL bPersist = FALSE;
228 #if !defined(_O_BINARY) && defined(O_BINARY)
229 # define _O_BINARY O_BINARY
230 # define _setmode setmode /* this is for BC4.5 ... */
232 _setmode(fileno(stdout), _O_BINARY);
234 for (i = 1; i < argc; i++) {
237 if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) {
238 printf("gnuplot %s patchlevel %s\n",
239 gnuplot_version, gnuplot_patchlevel);
241 } else if ((!stricmp(argv[i], "-noend")) || (!stricmp(argv[i], "/noend")) ||
242 (!stricmp(argv[i], "-persist"))) {
244 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
245 printf("Usage: gnuplot [OPTION] [FILE] [-]\n"
246 " -V, --version show gnuplot version\n"
247 " -h, --help show this help\n"
248 " -e \"cmd; cmd; ...\" prepand additional commands\n"
249 " -persist don't close the plot after executing FILE\n"
250 " -noend, /noend like -persist (non-portable Windows-only options)\n"
251 " - allow work in interactive mode after executing FILE\n"
252 "Only on Windows, -persist and - have the same effect.\n"
253 "This is gnuplot %s patchlevel %s\n"
254 "Report bugs to <http://sourceforge.net/projects/gnuplot>\n",
255 gnuplot_version, gnuplot_patchlevel);
260 /* CRS: create the new command line, passing all of the command
261 * line options to wgnuplot so that it can process them:
262 * first, get the command line,
263 * then move past the name of the program (e.g., 'pgnuplot'),
264 * finally, add what's left of the line onto the gnuplot command line. */
265 psCmdLine = GetCommandLine();
268 fprintf(stderr,"CmdLine: %s\n", psCmdLine);
269 fprintf(stderr,"argv[0]: %s\n",argv[0]);
272 /* CRS 30061999: Search for the first unquoted space. This should
273 separate the program name from the arguments. */
274 psCmdLine = FindUnquotedSpace(psCmdLine);
276 strncat(psGnuplotCommandLine, psCmdLine, MAX_PATH - strlen(psGnuplotCommandLine));
279 fprintf(stderr,"Arguments: %s\n", psCmdLine);
280 fprintf(stderr,"GnuplotCommandLine: %s\n",psGnuplotCommandLine);
283 /* CRS: if stdin isn't redirected then just launch wgnuplot normally
285 if (isatty(fileno(stdin))) {
286 if (WinExec(psGnuplotCommandLine, SW_SHOWDEFAULT) > 31) {
289 fprintf(stderr,"ERROR %u: Couldn't execute: \"%s\"\n",
290 GetLastError(), psGnuplotCommandLine);
294 /* CRS: initialize the STARTUPINFO and call CreateProcess(). */
295 siStartInfo.cb = sizeof(STARTUPINFO);
296 siStartInfo.lpReserved = NULL;
297 siStartInfo.lpReserved2 = NULL;
298 siStartInfo.cbReserved2 = 0;
299 siStartInfo.lpDesktop = NULL;
300 siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
301 siStartInfo.wShowWindow = SW_SHOWMINIMIZED;
303 bSuccess = CreateProcess(
304 NULL, /* pointer to name of executable module */
305 psGnuplotCommandLine, /* pointer to command line string */
306 NULL, /* pointer to process security attributes */
307 NULL, /* pointer to thread security attributes */
308 FALSE, /* handle inheritance flag */
309 0, /* creation flags */
310 NULL, /* pointer to new environment block */
311 NULL, /* pointer to current directory name */
312 &siStartInfo, /* pointer to STARTUPINFO */
313 &piProcInfo /* pointer to PROCESS_INFORMATION */
316 /* if CreateProcess() failed, print a warning and exit. */
318 fprintf(stderr,"ERROR %u: Couldn't execute: \"%s\"\n",
319 GetLastError(), psGnuplotCommandLine);
323 /* CRS: give gnuplot enough time to start (1 sec.) */
324 if (WaitForInputIdle(piProcInfo.hProcess, 1000)) {
325 fprintf(stderr, "Timeout: gnuplot is not ready\n");
329 /* CRS: get the HWND of the parent window and text windows */
330 EnumThreadWindows(piProcInfo.dwThreadId, cbGetTextWindow, 0);
332 /* CRS: free the process and thread handles */
333 CloseHandle(piProcInfo.hProcess);
334 CloseHandle(piProcInfo.hThread);
336 if (! hwndParent || ! hwndText) {
337 /* Still no gnuplot window? Problem! */
338 fprintf(stderr, "Can't find the gnuplot window");
342 /* wait for commands on stdin, and pass them on to the wgnuplot text
344 while (fgets(psBuffer, BUFFER_SIZE, stdin) != NULL) {
345 PostString(hwndText, psBuffer);
348 /* exit gracefully, unless -persist is requested */
350 /* CRS: Add a test to see if gnuplot is still running? */
351 PostString(hwndText, "\nexit\n");