wasCSharpSQLite – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * InterpSlaveCmd.java -- |
||
3 | * |
||
4 | * Implements the built-in "interp" Tcl command. |
||
5 | * |
||
6 | * Copyright (c) 2000 Christian Krone. |
||
7 | * |
||
8 | * See the file "license.terms" for information on usage and |
||
9 | * redistribution of this file, and for a DISCLAIMER OF ALL |
||
10 | * WARRANTIES. |
||
11 | * |
||
12 | * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart |
||
13 | * |
||
14 | * RCS @(#) $Id: InterpSlaveCmd.java,v 1.1 2000/08/20 06:08:43 mo Exp $ |
||
15 | * |
||
16 | */ |
||
17 | using System; |
||
18 | using System.Collections; |
||
19 | |||
20 | namespace tcl.lang |
||
21 | { |
||
22 | |||
23 | /// <summary> This class implements the slave interpreter commands, which are created |
||
24 | /// in response to the built-in "interp create" command in Tcl. |
||
25 | /// |
||
26 | /// It is also used by the "interp" command to record and find information |
||
27 | /// about slave interpreters. Maps from a command name in the master to |
||
28 | /// information about a slave interpreter, e.g. what aliases are defined |
||
29 | /// in it. |
||
30 | /// </summary> |
||
31 | |||
32 | class InterpSlaveCmd : CommandWithDispose, AssocData |
||
33 | { |
||
34 | |||
35 | private static readonly string[] options = new string[] { "alias", "aliases", "eval", "expose", "hide", "hidden", "issafe", "invokehidden", "marktrusted" }; |
||
36 | private const int OPT_ALIAS = 0; |
||
37 | private const int OPT_ALIASES = 1; |
||
38 | private const int OPT_EVAL = 2; |
||
39 | private const int OPT_EXPOSE = 3; |
||
40 | private const int OPT_HIDE = 4; |
||
41 | private const int OPT_HIDDEN = 5; |
||
42 | private const int OPT_ISSAFE = 6; |
||
43 | private const int OPT_INVOKEHIDDEN = 7; |
||
44 | private const int OPT_MARKTRUSTED = 8; |
||
45 | |||
46 | private static readonly string[] hiddenOptions = new string[] { "-global", "--" }; |
||
47 | private const int OPT_HIDDEN_GLOBAL = 0; |
||
48 | private const int OPT_HIDDEN_LAST = 1; |
||
49 | |||
50 | // Master interpreter for this slave. |
||
51 | |||
52 | internal Interp masterInterp; |
||
53 | |||
54 | // Hash entry in masters slave table for this slave interpreter. |
||
55 | // Used to find this record, and used when deleting the slave interpreter |
||
56 | // to delete it from the master's table. |
||
57 | |||
58 | internal string path; |
||
59 | |||
60 | // The slave interpreter. |
||
61 | |||
62 | internal Interp slaveInterp; |
||
63 | |||
64 | // Interpreter object command. |
||
65 | |||
66 | internal WrappedCommand interpCmd; |
||
67 | public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv ) |
||
68 | { |
||
69 | if ( objv.Length < 2 ) |
||
70 | { |
||
71 | throw new TclNumArgsException( interp, 1, objv, "cmd ?arg ...?" ); |
||
72 | } |
||
73 | int cmd = TclIndex.get( interp, objv[1], options, "option", 0 ); |
||
74 | |||
75 | switch ( cmd ) |
||
76 | { |
||
77 | |||
78 | case OPT_ALIAS: |
||
79 | if ( objv.Length == 3 ) |
||
80 | { |
||
81 | InterpAliasCmd.describe( interp, slaveInterp, objv[2] ); |
||
82 | return TCL.CompletionCode.RETURN; |
||
83 | } |
||
84 | |||
85 | if ( "".Equals( objv[3].ToString() ) ) |
||
86 | { |
||
87 | if ( objv.Length == 4 ) |
||
88 | { |
||
89 | InterpAliasCmd.delete( interp, slaveInterp, objv[2] ); |
||
90 | return TCL.CompletionCode.RETURN; |
||
91 | } |
||
92 | } |
||
93 | else |
||
94 | { |
||
95 | InterpAliasCmd.create( interp, slaveInterp, interp, objv[2], objv[3], 4, objv ); |
||
96 | return TCL.CompletionCode.RETURN; |
||
97 | } |
||
98 | throw new TclNumArgsException( interp, 2, objv, "aliasName ?targetName? ?args..?" ); |
||
99 | |||
100 | case OPT_ALIASES: |
||
101 | InterpAliasCmd.list( interp, slaveInterp ); |
||
102 | break; |
||
103 | |||
104 | case OPT_EVAL: |
||
105 | if ( objv.Length < 3 ) |
||
106 | { |
||
107 | throw new TclNumArgsException( interp, 2, objv, "arg ?arg ...?" ); |
||
108 | } |
||
109 | eval( interp, slaveInterp, 2, objv ); |
||
110 | break; |
||
111 | |||
112 | case OPT_EXPOSE: |
||
113 | if ( objv.Length < 3 || objv.Length > 4 ) |
||
114 | { |
||
115 | throw new TclNumArgsException( interp, 2, objv, "hiddenCmdName ?cmdName?" ); |
||
116 | } |
||
117 | expose( interp, slaveInterp, 2, objv ); |
||
118 | break; |
||
119 | |||
120 | case OPT_HIDE: |
||
121 | if ( objv.Length < 3 || objv.Length > 4 ) |
||
122 | { |
||
123 | throw new TclNumArgsException( interp, 2, objv, "cmdName ?hiddenCmdName?" ); |
||
124 | } |
||
125 | hide( interp, slaveInterp, 2, objv ); |
||
126 | break; |
||
127 | |||
128 | case OPT_HIDDEN: |
||
129 | if ( objv.Length != 2 ) |
||
130 | { |
||
131 | throw new TclNumArgsException( interp, 2, objv, null ); |
||
132 | } |
||
133 | InterpSlaveCmd.hidden( interp, slaveInterp ); |
||
134 | break; |
||
135 | |||
136 | case OPT_ISSAFE: |
||
137 | interp.setResult( slaveInterp.isSafe ); |
||
138 | break; |
||
139 | |||
140 | case OPT_INVOKEHIDDEN: |
||
141 | bool global = false; |
||
142 | int i; |
||
143 | for ( i = 2; i < objv.Length; i++ ) |
||
144 | { |
||
145 | |||
146 | if ( objv[i].ToString()[0] != '-' ) |
||
147 | { |
||
148 | break; |
||
149 | } |
||
150 | int index = TclIndex.get( interp, objv[i], hiddenOptions, "option", 0 ); |
||
151 | if ( index == OPT_HIDDEN_GLOBAL ) |
||
152 | { |
||
153 | global = true; |
||
154 | } |
||
155 | else |
||
156 | { |
||
157 | i++; |
||
158 | break; |
||
159 | } |
||
160 | } |
||
161 | if ( objv.Length - i < 1 ) |
||
162 | { |
||
163 | throw new TclNumArgsException( interp, 2, objv, "?-global? ?--? cmd ?arg ..?" ); |
||
164 | } |
||
165 | InterpSlaveCmd.invokeHidden( interp, slaveInterp, global, i, objv ); |
||
166 | break; |
||
167 | |||
168 | case OPT_MARKTRUSTED: |
||
169 | if ( objv.Length != 2 ) |
||
170 | { |
||
171 | throw new TclNumArgsException( interp, 2, objv, null ); |
||
172 | } |
||
173 | markTrusted( interp, slaveInterp ); |
||
174 | break; |
||
175 | } |
||
176 | return TCL.CompletionCode.RETURN; |
||
177 | } |
||
178 | /// <summary>---------------------------------------------------------------------- |
||
179 | /// |
||
180 | /// disposeCmd -- |
||
181 | /// |
||
182 | /// Invoked when an object command for a slave interpreter is deleted; |
||
183 | /// cleans up all state associated with the slave interpreter and destroys |
||
184 | /// the slave interpreter. |
||
185 | /// |
||
186 | /// Results: |
||
187 | /// None. |
||
188 | /// |
||
189 | /// Side effects: |
||
190 | /// Cleans up all state associated with the slave interpreter and |
||
191 | /// destroys the slave interpreter. |
||
192 | /// |
||
193 | /// ---------------------------------------------------------------------- |
||
194 | /// </summary> |
||
195 | |||
196 | public void disposeCmd() |
||
197 | { |
||
198 | // Unlink the slave from its master interpreter. |
||
199 | |||
200 | SupportClass.HashtableRemove( masterInterp.slaveTable, path ); |
||
201 | |||
202 | // Set to null so that when the InterpInfo is cleaned up in the slave |
||
203 | // it does not try to delete the command causing all sorts of grief. |
||
204 | // See SlaveRecordDeleteProc(). |
||
205 | |||
206 | interpCmd = null; |
||
207 | |||
208 | if ( slaveInterp != null ) |
||
209 | { |
||
210 | slaveInterp.dispose(); |
||
211 | } |
||
212 | } |
||
213 | public void disposeAssocData( Interp interp ) |
||
214 | // Current interpreter. |
||
215 | { |
||
216 | // There shouldn't be any commands left. |
||
217 | |||
218 | if ( !( interp.slaveTable.Count == 0 ) ) |
||
219 | { |
||
220 | System.Console.Error.WriteLine( "InterpInfoDeleteProc: still exist commands" ); |
||
221 | } |
||
222 | interp.slaveTable = null; |
||
223 | |||
224 | // Tell any interps that have aliases to this interp that they should |
||
225 | // delete those aliases. If the other interp was already dead, it |
||
226 | // would have removed the target record already. |
||
227 | |||
228 | // TODO ATK |
||
229 | foreach ( WrappedCommand slaveCmd in new ArrayList( interp.targetTable.Keys ) ) |
||
230 | { |
||
231 | Interp slaveInterp = (Interp)interp.targetTable[slaveCmd]; |
||
232 | slaveInterp.deleteCommandFromToken( slaveCmd ); |
||
233 | } |
||
234 | interp.targetTable = null; |
||
235 | |||
236 | if ( interp.interpChanTable != null ) |
||
237 | { |
||
238 | foreach ( Channel channel in new ArrayList( interp.interpChanTable.Values ) ) |
||
239 | { |
||
240 | TclIO.unregisterChannel( interp, channel ); |
||
241 | } |
||
242 | } |
||
243 | |||
244 | if ( interp.slave.interpCmd != null ) |
||
245 | { |
||
246 | // Tcl_DeleteInterp() was called on this interpreter, rather |
||
247 | // "interp delete" or the equivalent deletion of the command in the |
||
248 | // master. First ensure that the cleanup callback doesn't try to |
||
249 | // delete the interp again. |
||
250 | |||
251 | interp.slave.slaveInterp = null; |
||
252 | interp.slave.masterInterp.deleteCommandFromToken( interp.slave.interpCmd ); |
||
253 | } |
||
254 | |||
255 | // There shouldn't be any aliases left. |
||
256 | |||
257 | if ( !( interp.aliasTable.Count == 0 ) ) |
||
258 | { |
||
259 | System.Console.Error.WriteLine( "InterpInfoDeleteProc: still exist aliases" ); |
||
260 | } |
||
261 | interp.aliasTable = null; |
||
262 | } |
||
263 | internal static Interp create( Interp interp, TclObject path, bool safe ) |
||
264 | { |
||
265 | Interp masterInterp; |
||
266 | string pathString; |
||
267 | |||
268 | TclObject[] objv = TclList.getElements( interp, path ); |
||
269 | |||
270 | if ( objv.Length < 2 ) |
||
271 | { |
||
272 | masterInterp = interp; |
||
273 | |||
274 | pathString = path.ToString(); |
||
275 | } |
||
276 | else |
||
277 | { |
||
278 | TclObject obj = TclList.newInstance(); |
||
279 | |||
280 | TclList.insert( interp, obj, 0, objv, 0, objv.Length - 2 ); |
||
281 | masterInterp = InterpCmd.getInterp( interp, obj ); |
||
282 | |||
283 | pathString = objv[objv.Length - 1].ToString(); |
||
284 | } |
||
285 | if ( !safe ) |
||
286 | { |
||
287 | safe = masterInterp.isSafe; |
||
288 | } |
||
289 | |||
290 | if ( masterInterp.slaveTable.ContainsKey( pathString ) ) |
||
291 | { |
||
292 | throw new TclException( interp, "interpreter named \"" + pathString + "\" already exists, cannot create" ); |
||
293 | } |
||
294 | |||
295 | Interp slaveInterp = new Interp(); |
||
296 | InterpSlaveCmd slave = new InterpSlaveCmd(); |
||
297 | |||
298 | slaveInterp.slave = slave; |
||
299 | slaveInterp.setAssocData( "InterpSlaveCmd", slave ); |
||
300 | |||
301 | slave.masterInterp = masterInterp; |
||
302 | slave.path = pathString; |
||
303 | slave.slaveInterp = slaveInterp; |
||
304 | |||
305 | masterInterp.createCommand( pathString, slaveInterp.slave ); |
||
306 | slaveInterp.slave.interpCmd = NamespaceCmd.findCommand( masterInterp, pathString, null, 0 ); |
||
307 | |||
308 | SupportClass.PutElement( masterInterp.slaveTable, pathString, slaveInterp.slave ); |
||
309 | |||
310 | slaveInterp.setVar( "tcl_interactive", "0", TCL.VarFlag.GLOBAL_ONLY ); |
||
311 | |||
312 | // Inherit the recursion limit. |
||
313 | |||
314 | slaveInterp.maxNestingDepth = masterInterp.maxNestingDepth; |
||
315 | |||
316 | if ( safe ) |
||
317 | { |
||
318 | try |
||
319 | { |
||
320 | makeSafe( slaveInterp ); |
||
321 | } |
||
322 | catch ( TclException e ) |
||
323 | { |
||
324 | SupportClass.WriteStackTrace( e, Console.Error ); |
||
325 | } |
||
326 | } |
||
327 | else |
||
328 | { |
||
329 | //Tcl_Init(slaveInterp); |
||
330 | } |
||
331 | |||
332 | return slaveInterp; |
||
333 | } |
||
334 | internal static void eval( Interp interp, Interp slaveInterp, int objIx, TclObject[] objv ) |
||
335 | { |
||
336 | TCL.CompletionCode result; |
||
337 | |||
338 | slaveInterp.preserve(); |
||
339 | slaveInterp.allowExceptions(); |
||
340 | |||
341 | try |
||
342 | { |
||
343 | if ( objIx + 1 == objv.Length ) |
||
344 | { |
||
345 | slaveInterp.eval( objv[objIx], 0 ); |
||
346 | } |
||
347 | else |
||
348 | { |
||
349 | TclObject obj = TclList.newInstance(); |
||
350 | for ( int ix = objIx; ix < objv.Length; ix++ ) |
||
351 | { |
||
352 | TclList.append( interp, obj, objv[ix] ); |
||
353 | } |
||
354 | obj.preserve(); |
||
355 | slaveInterp.eval( obj, 0 ); |
||
356 | obj.release(); |
||
357 | } |
||
358 | result = slaveInterp.returnCode; |
||
359 | } |
||
360 | catch ( TclException e ) |
||
361 | { |
||
362 | result = e.getCompletionCode(); |
||
363 | } |
||
364 | |||
365 | slaveInterp.release(); |
||
366 | interp.transferResult( slaveInterp, result ); |
||
367 | } |
||
368 | internal static void expose( Interp interp, Interp slaveInterp, int objIx, TclObject[] objv ) |
||
369 | { |
||
370 | if ( interp.isSafe ) |
||
371 | { |
||
372 | throw new TclException( interp, "permission denied: " + "safe interpreter cannot expose commands" ); |
||
373 | } |
||
374 | |||
375 | int nameIdx = objv.Length - objIx == 1 ? objIx : objIx + 1; |
||
376 | |||
377 | try |
||
378 | { |
||
379 | |||
380 | slaveInterp.exposeCommand( objv[objIx].ToString(), objv[nameIdx].ToString() ); |
||
381 | } |
||
382 | catch ( TclException e ) |
||
383 | { |
||
384 | interp.transferResult( slaveInterp, e.getCompletionCode() ); |
||
385 | throw; |
||
386 | } |
||
387 | } |
||
388 | internal static void hide( Interp interp, Interp slaveInterp, int objIx, TclObject[] objv ) |
||
389 | { |
||
390 | if ( interp.isSafe ) |
||
391 | { |
||
392 | throw new TclException( interp, "permission denied: " + "safe interpreter cannot hide commands" ); |
||
393 | } |
||
394 | |||
395 | int nameIdx = objv.Length - objIx == 1 ? objIx : objIx + 1; |
||
396 | |||
397 | try |
||
398 | { |
||
399 | |||
400 | slaveInterp.hideCommand( objv[objIx].ToString(), objv[nameIdx].ToString() ); |
||
401 | } |
||
402 | catch ( TclException e ) |
||
403 | { |
||
404 | interp.transferResult( slaveInterp, e.getCompletionCode() ); |
||
405 | throw; |
||
406 | } |
||
407 | } |
||
408 | internal static void hidden( Interp interp, Interp slaveInterp ) |
||
409 | { |
||
410 | if ( slaveInterp.hiddenCmdTable == null ) |
||
411 | { |
||
412 | return; |
||
413 | } |
||
414 | |||
415 | TclObject result = TclList.newInstance(); |
||
416 | interp.setResult( result ); |
||
417 | |||
418 | IEnumerator hiddenCmds = slaveInterp.hiddenCmdTable.Keys.GetEnumerator(); |
||
419 | while ( hiddenCmds.MoveNext() ) |
||
420 | { |
||
421 | string cmdName = (string)hiddenCmds.Current; |
||
422 | TclList.append( interp, result, TclString.newInstance( cmdName ) ); |
||
423 | } |
||
424 | } |
||
425 | internal static void invokeHidden( Interp interp, Interp slaveInterp, bool global, int objIx, TclObject[] objv ) |
||
426 | { |
||
427 | TCL.CompletionCode result; |
||
428 | |||
429 | if ( interp.isSafe ) |
||
430 | { |
||
431 | throw new TclException( interp, "not allowed to " + "invoke hidden commands from safe interpreter" ); |
||
432 | } |
||
433 | |||
434 | slaveInterp.preserve(); |
||
435 | slaveInterp.allowExceptions(); |
||
436 | |||
437 | TclObject[] localObjv = new TclObject[objv.Length - objIx]; |
||
438 | for ( int i = 0; i < objv.Length - objIx; i++ ) |
||
439 | { |
||
440 | localObjv[i] = objv[i + objIx]; |
||
441 | } |
||
442 | |||
443 | try |
||
444 | { |
||
445 | if ( global ) |
||
446 | { |
||
447 | slaveInterp.invokeGlobal( localObjv, Interp.INVOKE_HIDDEN ); |
||
448 | } |
||
449 | else |
||
450 | { |
||
451 | slaveInterp.invoke( localObjv, Interp.INVOKE_HIDDEN ); |
||
452 | } |
||
453 | result = slaveInterp.returnCode; |
||
454 | } |
||
455 | catch ( TclException e ) |
||
456 | { |
||
457 | result = e.getCompletionCode(); |
||
458 | } |
||
459 | |||
460 | slaveInterp.release(); |
||
461 | interp.transferResult( slaveInterp, result ); |
||
462 | } |
||
463 | internal static void markTrusted( Interp interp, Interp slaveInterp ) |
||
464 | { |
||
465 | if ( interp.isSafe ) |
||
466 | { |
||
467 | throw new TclException( interp, "permission denied: " + "safe interpreter cannot mark trusted" ); |
||
468 | } |
||
469 | slaveInterp.isSafe = false; |
||
470 | } |
||
471 | private static void makeSafe( Interp interp ) |
||
472 | { |
||
473 | Channel chan; // Channel to remove from safe interpreter. |
||
474 | |||
475 | interp.hideUnsafeCommands(); |
||
476 | |||
477 | interp.isSafe = true; |
||
478 | |||
479 | // Unsetting variables : (which should not have been set |
||
480 | // in the first place, but...) |
||
481 | |||
482 | // No env array in a safe slave. |
||
483 | |||
484 | try |
||
485 | { |
||
486 | interp.unsetVar( "env", TCL.VarFlag.GLOBAL_ONLY ); |
||
487 | } |
||
488 | catch ( TclException e ) |
||
489 | { |
||
490 | } |
||
491 | |||
492 | // Remove unsafe parts of tcl_platform |
||
493 | |||
494 | try |
||
495 | { |
||
496 | interp.unsetVar( "tcl_platform", "os", TCL.VarFlag.GLOBAL_ONLY ); |
||
497 | } |
||
498 | catch ( TclException e ) |
||
499 | { |
||
500 | } |
||
501 | try |
||
502 | { |
||
503 | interp.unsetVar( "tcl_platform", "osVersion", TCL.VarFlag.GLOBAL_ONLY ); |
||
504 | } |
||
505 | catch ( TclException e ) |
||
506 | { |
||
507 | } |
||
508 | try |
||
509 | { |
||
510 | interp.unsetVar( "tcl_platform", "machine", TCL.VarFlag.GLOBAL_ONLY ); |
||
511 | } |
||
512 | catch ( TclException e ) |
||
513 | { |
||
514 | } |
||
515 | try |
||
516 | { |
||
517 | interp.unsetVar( "tcl_platform", "user", TCL.VarFlag.GLOBAL_ONLY ); |
||
518 | } |
||
519 | catch ( TclException e ) |
||
520 | { |
||
521 | } |
||
522 | |||
523 | // Unset path informations variables |
||
524 | // (the only one remaining is [info nameofexecutable]) |
||
525 | |||
526 | try |
||
527 | { |
||
528 | interp.unsetVar( "tclDefaultLibrary", TCL.VarFlag.GLOBAL_ONLY ); |
||
529 | } |
||
530 | catch ( TclException e ) |
||
531 | { |
||
532 | } |
||
533 | try |
||
534 | { |
||
535 | interp.unsetVar( "tcl_library", TCL.VarFlag.GLOBAL_ONLY ); |
||
536 | } |
||
537 | catch ( TclException e ) |
||
538 | { |
||
539 | } |
||
540 | try |
||
541 | { |
||
542 | interp.unsetVar( "tcl_pkgPath", TCL.VarFlag.GLOBAL_ONLY ); |
||
543 | } |
||
544 | catch ( TclException e ) |
||
545 | { |
||
546 | } |
||
547 | |||
548 | // Remove the standard channels from the interpreter; safe interpreters |
||
549 | // do not ordinarily have access to stdin, stdout and stderr. |
||
550 | // |
||
551 | // NOTE: These channels are not added to the interpreter by the |
||
552 | // Tcl_CreateInterp call, but may be added later, by another I/O |
||
553 | // operation. We want to ensure that the interpreter does not have |
||
554 | // these channels even if it is being made safe after being used for |
||
555 | // some time.. |
||
556 | |||
557 | chan = TclIO.getStdChannel( StdChannel.STDIN ); |
||
558 | if ( chan != null ) |
||
559 | { |
||
560 | TclIO.unregisterChannel( interp, chan ); |
||
561 | } |
||
562 | chan = TclIO.getStdChannel( StdChannel.STDOUT ); |
||
563 | if ( chan != null ) |
||
564 | { |
||
565 | TclIO.unregisterChannel( interp, chan ); |
||
566 | } |
||
567 | chan = TclIO.getStdChannel( StdChannel.STDERR ); |
||
568 | if ( chan != null ) |
||
569 | { |
||
570 | TclIO.unregisterChannel( interp, chan ); |
||
571 | } |
||
572 | } |
||
573 | } // end InterpSlaveCmd |
||
574 | } |