PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plserver.c
Go to the documentation of this file.
1 // Copyright 1993, 1994, 1995
2 // Maurice LeBrun mjl@dino.ph.utexas.edu
3 // Institute for Fusion Studies University of Texas at Austin
4 //
5 // Copyright (C) 2004 Joao Cardoso
6 //
7 // This file is part of PLplot.
8 //
9 // PLplot is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Library General Public License as published
11 // by the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // PLplot is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Library General Public License for more details.
18 //
19 // You should have received a copy of the GNU Library General Public License
20 // along with PLplot; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 //--------------------------------------------------------------------------
24 //
25 // PLplot graphics server.
26 //
27 // Just a front-end to the pltkMain() function. Structured along the
28 // preferred lines for extended wish'es. Is typically run as a child
29 // process from the PLplot TK driver to render output. Can use either TK
30 // send or Tcl-DP RPC for communication, depending on how it is invoked.
31 //
32 // Note that plserver can be used the same way as wish or dpwish, as it
33 // contains the functionality of each of these (except the -notk Tcl-DP
34 // command-line option is not supported).
35 //
36 
37 #define NEED_PLDEBUG
38 #include "plserver.h"
39 
40 // Application-specific command-line options
41 // Variable declarations
42 
43 static char *client_name; // Name of client main window
44 static char *auto_path; // addition to auto_path
45 static int child; // set if child of TK driver
46 #ifdef PLD_dp
47 static int dp; // set if using Tcl-DP to communicate
48 #endif
49 static char *client_host; // Host id for client
50 static char *client_port; // Communications port id for client
51 
52 static Tk_ArgvInfo argTable[] = {
53  { "-client_name", TK_ARGV_STRING, (char *) NULL, (char *) &client_name,
54  "Client main window name to connect to" },
55  { "-client_host", TK_ARGV_STRING, (char *) NULL, (char *) &client_host,
56  "Client host to connect to" },
57  { "-client_port", TK_ARGV_STRING, (char *) NULL, (char *) &client_port,
58  "Client port (Tcl-DP) to connect to" },
59  { "-auto_path", TK_ARGV_STRING, (char *) NULL, (char *) &auto_path,
60  "Additional directory(s) to autoload" },
61  { "-child", TK_ARGV_CONSTANT, (char *) 1, (char *) &child,
62  "Set ONLY when child of PLplot TK driver" },
63  { (char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL,
64  (char *) NULL }
65 };
66 
67 // PLplot/Tk extension command -- handle exit.
68 
69 static int
70 plExitCmd( ClientData clientData, Tcl_Interp *interp, int argc, char **argv );
71 
72 // Evals the specified command, aborting on an error.
73 
74 static void
75 tcl_cmd( Tcl_Interp *interp, const char *cmd );
76 
77 // Application-specific startup
78 
79 static int
80 AppInit( Tcl_Interp *interp );
81 
82 //--------------------------------------------------------------------------
83 // main --
84 //
85 // Just a stub routine to call pltkMain. The latter is nice to have
86 // when building extended wishes, since then you don't have to rely on
87 // sucking the Tk main out of libtk (which doesn't work correctly on all
88 // systems/compilers/linkers/etc). Hopefully in the future Tk will
89 // supply a sufficiently capable tkMain() type function that can be used
90 // instead.
91 //--------------------------------------------------------------------------
92 
93 int
94 main( int argc, const char **argv )
95 {
96  int i, myargc = argc;
97  const char **myargv;
98  Tcl_Interp *interp;
99  const char *helpmsg = "Command-specific options:";
100 
101 #ifdef DEBUG
102  fprintf( stderr, "Program %s called with arguments :\n", argv[0] );
103  for ( i = 1; i < argc; i++ )
104  {
105  fprintf( stderr, "%s ", argv[i] );
106  }
107  fprintf( stderr, "\n" );
108 #endif
109 
110 // Create interpreter just for argument parsing
111 
112  interp = Tcl_CreateInterp();
113 
114 // Save arglist to get around Tk_ParseArgv limitations
115 
116 #ifdef DEBUG
117  fprintf( stderr, "Before myargv\n" );
118 #endif
119 
120  // According to both Linux and Windows documentation,
121  // argv is actually argc+1 in length with the last element set to
122  // the NULL value. So leave room for that last element.
123  myargv = (const char **) malloc( ( argc + 1 ) * sizeof ( char * ) );
124  for ( i = 0; i < argc + 1; i++ )
125  {
126  myargv[i] = argv[i];
127  }
128 
129 #ifdef DEBUG
130  fprintf( stderr, "After myargv\n" );
131 #endif
132 
133 // Parse args
134 // Examine the result string to see if an error return is really an error
135 
136  if ( Tk_ParseArgv( interp, (Tk_Window) NULL, &argc, argv,
137  argTable, TK_ARGV_NO_DEFAULTS ) != TCL_OK )
138  {
139 #ifdef DEBUG
140  fprintf( stderr, "Error in Tk_ParseArgv\n" );
141 #endif
142  fprintf( stderr, "\n(plserver) %s\n\n", Tcl_GetStringResult( interp ) );
143  fprintf( stderr, "\
144 The client_<xxx> and -child options should not be used except via the\n\
145 PLplot/Tk driver.\n\n(wish) " );
146 #ifdef DEBUG
147  fprintf( stderr, "Before Tcl_SetResult\n" );
148 #endif
149  Tcl_SetResult( interp, (char *) helpmsg, TCL_VOLATILE );
150  }
151 
152 #ifdef DEBUG
153  fprintf( stderr, "After Tk_ParseArgv\n" );
154 #endif
155 
156 // No longer need interpreter
157 
158 #if TCL_MAJOR_VERSION < 7 || ( TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION < 5 )
159  Tcl_DeleteInterp( interp );
160 #endif
161 
162 // Call pltkMain() with original argc/argv list, to make sure -h is seen
163 // Does not return until program exit
164 
165  exit( pltkMain( myargc, myargv, NULL, AppInit ) );
166 }
167 
168 
169 //
170 //--------------------------------------------------------------------------
171 //
172 // AppInit --
173 //
174 // This procedure performs application-specific initialization.
175 // Most applications, especially those that incorporate additional
176 // packages, will have their own version of this procedure.
177 //
178 // Results:
179 // Returns a standard Tcl completion code, and leaves an error
180 // message in interp->result if an error occurs.
181 //
182 // Side effects:
183 // Depends on the startup script.
184 //
185 //--------------------------------------------------------------------------
186 //
187 
188 static int
189 AppInit( Tcl_Interp *interp )
190 {
191  Tk_Window mainWindow = Tk_MainWindow( interp );
192 
193 //
194 // Call the init procedures for included packages. Each call should
195 // look like this:
196 //
197 // if (Mod_Init(interp) == TCL_ERROR) {
198 // return TCL_ERROR;
199 // }
200 //
201 // where "Mod" is the name of the module.
202 //
203  if ( Pltk_Init( interp ) == TCL_ERROR )
204  {
205  return TCL_ERROR;
206  }
207 
208 // Application-specific startup. That means: for use in plserver ONLY.
209 
210 // Pass child variable to interpreter if set.
211 
212  if ( child != 0 )
213  Tcl_SetVar( interp, "child", "1", 0 );
214 
215 // If client_name is set, TK send is being used to communicate.
216 // If client_port is set, Tcl-DP RPC is being used to communicate.
217 // The "dp" variable determines which style communication is used
218 
219  if ( client_name != NULL )
220  {
221  Tcl_SetVar( interp, "client_name", client_name, 0 );
222  tcl_cmd( interp, "set dp 0" );
223 #ifdef PLD_dp
224  dp = 0;
225 #endif
226  }
227  else if ( client_port != NULL )
228  {
229 #ifdef PLD_dp
230  Tcl_SetVar( interp, "client_port", client_port, 0 );
231  if ( client_host != NULL )
232  Tcl_SetVar( interp, "client_host", client_host, 0 );
233  dp = 1; tcl_cmd( interp, "set dp 1" );
234 #else
235  Tcl_AppendResult( interp,
236  "no Tcl-DP support in this version of plserver",
237  (char *) NULL );
238  return TCL_ERROR;
239 #endif
240  }
241 
242 // Add user-specified directory(s) to auto_path
243 
244  if ( auto_path != NULL )
245  {
246  Tcl_SetVar( interp, "dir", auto_path, 0 );
247  tcl_cmd( interp, "lappend auto_path $dir" );
248  }
249 
250 // Rename "exit" to "tkexit", and insert custom exit handler
251 
252  tcl_cmd( interp, "rename exit tkexit" );
253 
254  Tcl_CreateCommand( interp, "exit", (Tcl_CmdProc *) plExitCmd,
255  (ClientData) mainWindow, (Tcl_CmdDeleteProc *) NULL );
256 
257  return TCL_OK;
258 }
259 
260 //--------------------------------------------------------------------------
261 // plExitCmd
262 //
263 // PLplot/Tk extension command -- handle exit.
264 // The reason for overriding the normal exit command is so we can tell the
265 // client to abort.
266 //--------------------------------------------------------------------------
267 
268 static int
269 plExitCmd( ClientData PL_UNUSED( clientData ), Tcl_Interp *interp, int argc, char **argv )
270 {
271  int value = 0;
272  const char *res;
273 
274 // Print error message if one given
275 
276  res = Tcl_GetStringResult( interp );
277  if ( res[0] != '\0' )
278  fprintf( stderr, "%s\n", res );
279 
280 // Best to check the syntax before proceeding
281 
282  if ( ( argc != 1 ) && ( argc != 2 ) )
283  {
284  Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0],
285  " ?returnCode?\"", (char *) NULL );
286  return TCL_ERROR;
287  }
288  if ( ( argc != 1 ) && ( Tcl_GetInt( interp, argv[1], &value ) != TCL_OK ) )
289  {
290  Tcl_AppendResult( interp, "non-integer return code: \"", argv[1],
291  "\"", (char *) NULL );
292  return TCL_ERROR;
293  }
294 
295 // If client exists, tell it to self destruct
296 
297  Tcl_VarEval( interp, "plserver_link_end", (char **) NULL );
298 
299 // Now really exit
300 // (Note: this function is actually deprecated, but as it is only used here
301 // at the end of the program, let's leave it.)
302 
303  if ( argc == 1 )
304  {
305  return Tcl_VarEval( interp, "tkexit", (char **) NULL );
306  }
307  else
308  {
309  return Tcl_VarEval( interp, "tkexit", argv[1], (char **) NULL );
310  }
311 }
312 
313 //--------------------------------------------------------------------------
314 // tcl_cmd
315 //
316 // Evals the specified command, aborting on an error.
317 //--------------------------------------------------------------------------
318 
319 static void
320 tcl_cmd( Tcl_Interp *interp, const char *cmd )
321 {
322  int result;
323 
324  dbug_enter( "tcl_cmd" );
325  pldebug( "tcl_cmd", "evaluating command %s\n", cmd );
326 
327  result = Tcl_VarEval( interp, cmd, (char **) NULL );
328  if ( result != TCL_OK )
329  {
330  Tcl_Eval( interp, "exit" );
331  exit( 1 );
332  }
333 }