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