OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [interp.cxx] - Blame information for rev 321

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 26 unneback
//{{{  Banner                                                   
2
 
3
//============================================================================
4
//
5
//      interp.cxx
6
//
7
//      Provide access to Tcl interpreters
8
//
9
//============================================================================
10
//####COPYRIGHTBEGIN####
11
//                                                                          
12
// ----------------------------------------------------------------------------
13
// Copyright (C) 2002 Bart Veer
14
// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
15
//
16
// This file is part of the eCos host tools.
17
//
18
// This program is free software; you can redistribute it and/or modify it 
19
// under the terms of the GNU General Public License as published by the Free 
20
// Software Foundation; either version 2 of the License, or (at your option) 
21
// any later version.
22
// 
23
// This program is distributed in the hope that it will be useful, but WITHOUT 
24
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
25
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
26
// more details.
27
// 
28
// You should have received a copy of the GNU General Public License along with
29
// this program; if not, write to the Free Software Foundation, Inc., 
30
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31
//
32
// ----------------------------------------------------------------------------
33
//                                                                          
34
//####COPYRIGHTEND####
35
//============================================================================
36
//#####DESCRIPTIONBEGIN####
37
//
38
// Author(s):   bartv
39
// Contact(s):  bartv
40
// Date:        1999/01/20
41
// Version:     0.02
42
//
43
//####DESCRIPTIONEND####
44
//============================================================================
45
 
46
//}}}
47
//{{{  #include's                                               
48
 
49
// ----------------------------------------------------------------------------
50
#include "cdlconfig.h"
51
 
52
// Get the infrastructure types, assertions, tracing and similar
53
// facilities.
54
#include <cyg/infra/cyg_ass.h>
55
#include <cyg/infra/cyg_trac.h>
56
 
57
// <cdl.hxx> defines everything implemented in this module.
58
// It implicitly supplies <string>, <vector> and <map> because
59
// the class definitions rely on these headers. It also brings
60
// in <tcl.h>
61
#include <cdlcore.hxx>
62
 
63
//}}}
64
 
65
//{{{  Statics                                                  
66
 
67
// ----------------------------------------------------------------------------
68
// This key is used for accessing AssocData in the Tcl interpreters,
69
// specifically the CdlInterpreter object.
70
char* CdlInterpreterBody::cdlinterpreter_assoc_data_key = "__cdlinterpreter";
71
 
72
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlInterpreterBody);
73
 
74
//}}}
75
//{{{  CdlInterpreter:: creation                                
76
 
77
// ----------------------------------------------------------------------------
78
// Default constructor. This will only get invoked via the make() static
79
// member.
80
 
81
CdlInterpreterBody::CdlInterpreterBody(Tcl_Interp* tcl_interp_arg)
82
{
83
    CYG_REPORT_FUNCNAME("CdlInterpreter:: default constructor");
84
    CYG_REPORT_FUNCARG2XV(this, tcl_interp_arg);
85
    CYG_PRECONDITIONC(0 != tcl_interp_arg);
86
 
87
    tcl_interp          = tcl_interp_arg;
88
    owns_interp         = false;
89
    parent              = 0;
90
    toplevel            = 0;
91
    transaction         = 0;
92
    loadable            = 0;
93
    container           = 0;
94
    node                = 0;
95
    context             = "";
96
    error_fn_ptr        = 0;
97
    warning_fn_ptr      = 0;
98
    current_commands    = 0;
99
    cdl_result          = false;
100
 
101
    CYGDBG_MEMLEAK_CONSTRUCTOR();
102
    cdlinterpreterbody_cookie   = CdlInterpreterBody_Magic;
103
 
104
    Tcl_SetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0, static_cast<ClientData>(this));
105
 
106
 
107
    CYG_POSTCONDITION_THISC();
108
    CYG_REPORT_RETURN();
109
}
110
 
111
// ----------------------------------------------------------------------------
112
// Create a new CDL interpreter. The underlying Tcl interpreter can be
113
// supplied by the caller, or else a suitable interpreter will be created
114
// with default settings. This default interpreter will only support Tcl,
115
// not Tk. There is no call to any AppInit() function, no support for
116
// autoloading packages, the "unknown" command is not implemented, and
117
// no command files will be read in.
118
//
119
// It is convenient to provide immediate access to two Tcl variables,
120
// cdl_version and cdl_interactive.
121
 
122
CdlInterpreter
123
CdlInterpreterBody::make(Tcl_Interp* tcl_interp_arg)
124
{
125
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::make", "interpreter %p");
126
    CYG_REPORT_FUNCARG1XV(tcl_interp_arg);
127
 
128
    Tcl_Interp* tcl_interp = tcl_interp_arg;
129
    if (0 == tcl_interp) {
130
        tcl_interp = Tcl_CreateInterp();
131
        if (0 == tcl_interp) {
132
            throw std::bad_alloc();
133
        }
134
    } else {
135
        // Make sure that this Tcl interpreter is not already used
136
        // for another CdlInterpreter object.
137
        ClientData tmp = Tcl_GetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0);
138
        if (0 != tmp) {
139
            CYG_FAIL("Attempt to use a Tcl interpreter for multiple CDL interpreters");
140
            throw std::bad_alloc();
141
        }
142
    }
143
 
144
    CdlInterpreter result = 0;
145
    try {
146
        result = new CdlInterpreterBody(tcl_interp);
147
 
148
        std::string version = Cdl::get_library_version();
149
        if (0 == Tcl_SetVar(tcl_interp, "cdl_version", CDL_TCL_CONST_CAST(char*,version.c_str()), TCL_GLOBAL_ONLY)) {
150
            throw std::bad_alloc();
151
        }
152
        if (0 == Tcl_SetVar(tcl_interp, "cdl_interactive", CDL_TCL_CONST_CAST(char*, (Cdl::is_interactive() ? "1" : "0")),
153
                            TCL_GLOBAL_ONLY)) {
154
            throw std::bad_alloc();
155
        }
156
    }
157
    catch(std::bad_alloc) {
158
        if (0 == tcl_interp_arg) {
159
            Tcl_DeleteInterp(tcl_interp);
160
        }
161
        throw;
162
    }
163
    if (0 == tcl_interp_arg) {
164
        result->owns_interp     = true;
165
    }
166
    CYG_POSTCONDITION_CLASSC(result);
167
    CYG_REPORT_RETVAL(result);
168
    return result;
169
}
170
 
171
// ----------------------------------------------------------------------------
172
// Given a toplevel and a loadable, create a new slave interpreter
173
// for that loadable. There should be master interpreter associated
174
// with the toplevel already.
175
//
176
// FIXME: do slave interpreters automatically see cdl_version and
177
// cdl_interactive?
178
 
179
CdlInterpreter
180
CdlInterpreterBody::create_slave(CdlLoadable loadable_arg, bool safe)
181
{
182
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::create_slave", "slave %p");
183
    CYG_REPORT_FUNCARG3XV(this, loadable_arg, safe);
184
    CYG_PRECONDITION_THISC();
185
    CYG_PRECONDITION(0 == parent, "slave interpreters cannot be created inside slaves");
186
    CYG_PRECONDITION(0 != toplevel, "CDL's slave interpreters need an associated toplevel");
187
    CYG_PRECONDITION_CLASSC(loadable_arg);
188
 
189
    // Slave interpreters need a name. Use a counter to create them uniquely.
190
    static cdl_int      next_slave = 1;
191
    std::string         slave_name;
192
    Cdl::integer_to_string(next_slave++, slave_name);
193
    slave_name = "slave" + slave_name;
194
 
195
    // FIXME: creating a slave that is not safe appears to fail.
196
#if 0    
197
    Tcl_Interp* slave = Tcl_CreateSlave(interp, CDL_TCL_CONST_CAST(char*, slave_name.c_str()), safe);
198
#else
199
    Tcl_Interp* slave = Tcl_CreateInterp();
200
#endif
201
    if (0 == slave) {
202
        throw std::bad_alloc();
203
    }
204
 
205
    CdlInterpreter result = 0;
206
    try {
207
        result = new CdlInterpreterBody(slave);
208
    }
209
    catch(std::bad_alloc) {
210
        Tcl_DeleteInterp(slave);
211
        throw;
212
    }
213
    result->owns_interp = true;
214
#if 0    
215
    try {
216
        slaves.push_back(result);
217
    }
218
    catch(std::bad_alloc) {
219
        delete result;
220
        throw;
221
    }
222
#endif
223
 
224
    result->parent      = this;
225
    result->set_toplevel(toplevel);
226
    result->loadable    = loadable_arg;
227
    result->set_variable("cdl_version", get_variable("cdl_version"));
228
    result->set_variable("cdl_interactive", get_variable("cdl_interactive"));
229
 
230
    CYG_POSTCONDITION_CLASSC(result);
231
    CYG_REPORT_RETVAL(result);
232
    return result;
233
}
234
 
235
// ----------------------------------------------------------------------------
236
// Given an existing interpreter, turn it into a safe one. This is a one-way
237
// transformation.
238
void
239
CdlInterpreterBody::make_safe(void)
240
{
241
    CYG_REPORT_FUNCNAME("CdlInterpreter::make_safe");
242
    CYG_PRECONDITION_THISC();
243
 
244
    if (0 != Tcl_MakeSafe(tcl_interp)) {
245
        throw std::bad_alloc();
246
    }
247
    CYG_REPORT_RETURN();
248
}
249
 
250
//}}}
251
//{{{  CdlInterpreter:: destructor                              
252
 
253
// ----------------------------------------------------------------------------
254
// Default destructor. It is necessary to worry about any slave
255
// interpreters, but otherwise there are no complications.
256
 
257
CdlInterpreterBody::~CdlInterpreterBody()
258
{
259
    CYG_REPORT_FUNCNAME("CdlInterpreter:: destructor");
260
    CYG_REPORT_FUNCARG1XV(this);
261
    CYG_PRECONDITION_THISC();
262
 
263
    cdlinterpreterbody_cookie   = CdlInterpreterBody_Invalid;
264
    parent                      = 0;
265
    toplevel                    = 0;
266
    transaction                 = 0;
267
    loadable                    = 0;
268
    container                   = 0;
269
    node                        = 0;
270
    context                     = "";
271
    error_fn_ptr                = 0;
272
    warning_fn_ptr              = 0;
273
    current_commands            = 0;
274
    cdl_result                  = false;
275
 
276
    // Make sure slave interpreters get deleted before the current one
277
    for (std::vector<CdlInterpreter>::iterator i = slaves.begin(); i != slaves.end(); i++) {
278
        delete *i;
279
        *i = 0;
280
    }
281
 
282
    Tcl_DeleteAssocData(tcl_interp, cdlinterpreter_assoc_data_key);
283
    if (owns_interp) {
284
        Tcl_DeleteInterp(tcl_interp);
285
    }
286
    owns_interp = false;
287
    tcl_interp  = 0;
288
    CYGDBG_MEMLEAK_DESTRUCTOR();
289
 
290
    CYG_REPORT_RETURN();
291
}
292
 
293
//}}}
294
//{{{  CdlInterpreter:: check_this()                            
295
 
296
// ----------------------------------------------------------------------------
297
// check_this().
298
 
299
bool
300
CdlInterpreterBody::check_this(cyg_assert_class_zeal zeal) const
301
{
302
    if (CdlInterpreterBody_Magic != cdlinterpreterbody_cookie)
303
        return false;
304
 
305
    CYGDBG_MEMLEAK_CHECKTHIS();
306
 
307
    switch(zeal) {
308
      case cyg_system_test  :
309
      case cyg_extreme      :
310
          if (slaves.size() > 0) {
311
              for (std::vector<CdlInterpreter>::const_iterator i = slaves.begin(); i != slaves.end(); i++) {
312
                  if (!(*i)->check_this(cyg_quick)) {
313
                      return false;
314
                  }
315
              }
316
          }
317
      case cyg_thorough     :
318
          if ((0 != toplevel) && !toplevel->check_this(cyg_quick)) {
319
              return false;
320
          }
321
          if ((0 != transaction) && !transaction->check_this(cyg_quick)) {
322
              return false;
323
          }
324
          if ((0 != loadable) && !loadable->check_this(cyg_quick)) {
325
              return false;
326
          }
327
          if ((0 != container) && !container->check_this(cyg_quick)) {
328
              return false;
329
          }
330
          if ((0 != node) && !node->check_this(cyg_quick)) {
331
              return false;
332
          }
333
      case cyg_quick        :
334
          // For now only the toplevel interpreter should have slaves.
335
          if ((0 != parent) && (slaves.size() > 0)) {
336
              return false;
337
          }
338
          if( 0 == tcl_interp) {
339
              return false;
340
          }
341
      case cyg_trivial      :
342
      case cyg_none         :
343
          break;
344
    }
345
    return true;
346
}
347
 
348
//}}}
349
//{{{  CdlInterpreter:: set_toplevel() etc.                     
350
 
351
// ----------------------------------------------------------------------------
352
// Keep track of the current toplevel, container, etc. This gives commands
353
// added to the Tcl interpreter a simple way of figuring out the current
354
// state of the world so that properties get added to the right node, etc.
355
//
356
// set_toplevel() should only be called once, for the master interpreter
357
// associated with a toplevel. All slave interpreters inherit this value.
358
//
359
// There is no set_loadable(), instead the loadable field is filled in
360
// by create_slave() and cannot be changed.
361
 
362
CdlToplevel
363
CdlInterpreterBody::get_toplevel() const
364
{
365
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_toplevel", "result %p");
366
    CYG_REPORT_FUNCARG1XV(this);
367
    CYG_PRECONDITION_THISC();
368
 
369
    CdlToplevel result = toplevel;
370
    CYG_REPORT_RETVAL(result);
371
    return result;
372
}
373
 
374
CdlTransaction
375
CdlInterpreterBody::get_transaction() const
376
{
377
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_transaction", "result %p");
378
    CYG_REPORT_FUNCARG1XV(this);
379
    CYG_PRECONDITION_THISC();
380
 
381
    CdlTransaction result = transaction;
382
    CYG_REPORT_RETVAL(result);
383
    return result;
384
}
385
 
386
CdlLoadable
387
CdlInterpreterBody::get_loadable() const
388
{
389
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter:get_loadable", "result %p");
390
    CYG_REPORT_FUNCARG1XV(this);
391
    CYG_PRECONDITION_THISC();
392
 
393
    CdlLoadable result = loadable;
394
    CYG_REPORT_RETVAL(result);
395
    return result;
396
}
397
 
398
CdlContainer
399
CdlInterpreterBody::get_container() const
400
{
401
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_container", "result %p");
402
    CYG_REPORT_FUNCARG1XV(this);
403
    CYG_PRECONDITION_THISC();
404
 
405
    CdlContainer result = container;
406
    CYG_REPORT_RETVAL(result);
407
    return result;
408
}
409
 
410
CdlNode
411
CdlInterpreterBody::get_node() const
412
{
413
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_node", "result %p");
414
    CYG_REPORT_FUNCARG1XV(this);
415
    CYG_PRECONDITION_THISC();
416
 
417
    CdlNode result = node;
418
    CYG_REPORT_RETVAL(result);
419
    return result;
420
}
421
 
422
std::string
423
CdlInterpreterBody::get_context() const
424
{
425
    CYG_REPORT_FUNCNAME("CdlInterpreter::get_context");
426
    CYG_REPORT_FUNCARG1XV(this);
427
    CYG_PRECONDITION_THISC();
428
 
429
    CYG_REPORT_RETURN();
430
    return context;
431
}
432
 
433
CdlDiagnosticFnPtr
434
CdlInterpreterBody::get_error_fn_ptr() const
435
{
436
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_error_fn_ptr", "result %p");
437
    CYG_REPORT_FUNCARG1XV(this);
438
    CYG_PRECONDITION_THISC();
439
 
440
    CdlDiagnosticFnPtr result = error_fn_ptr;
441
    CYG_REPORT_RETVAL(result);
442
    return result;
443
}
444
 
445
CdlDiagnosticFnPtr
446
CdlInterpreterBody::get_warning_fn_ptr() const
447
{
448
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_warning_fn_ptr", "result %p");
449
    CYG_REPORT_FUNCARG1XV(this);
450
    CYG_PRECONDITION_THISC();
451
 
452
    CdlDiagnosticFnPtr result = warning_fn_ptr;
453
    CYG_REPORT_RETVAL(result);
454
    return result;
455
}
456
 
457
void
458
CdlInterpreterBody::set_toplevel(CdlToplevel new_toplevel)
459
{
460
    CYG_REPORT_FUNCNAME("CdlInterpreter::set_toplevel");
461
    CYG_REPORT_FUNCARG2XV(this, new_toplevel);
462
    CYG_PRECONDITION_THISC();
463
    CYG_PRECONDITION(0 == toplevel, "changing toplevels is not allowed");
464
    CYG_PRECONDITION_CLASSC(new_toplevel);
465
 
466
    toplevel = new_toplevel;
467
    CYG_REPORT_RETURN();
468
}
469
 
470
void
471
CdlInterpreterBody::set_transaction(CdlTransaction new_transaction)
472
{
473
    CYG_REPORT_FUNCNAME("CdlInterpreter::set_transaction");
474
    CYG_REPORT_FUNCARG2XV(this, new_transaction);
475
    CYG_PRECONDITION_THISC();
476
    CYG_PRECONDITION_ZERO_OR_CLASSC(new_transaction);
477
 
478
    transaction = new_transaction;
479
    CYG_REPORT_RETURN();
480
}
481
 
482
CdlContainer
483
CdlInterpreterBody::push_container(CdlContainer new_container)
484
{
485
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_container", "result %p");
486
    CYG_REPORT_FUNCARG2XV(this, new_container);
487
    CYG_PRECONDITION_THISC();
488
    CYG_PRECONDITION_CLASSC(new_container);
489
 
490
    CdlContainer result = container;
491
    container = new_container;
492
    CYG_REPORT_RETVAL(result);
493
    return result;
494
}
495
 
496
void
497
CdlInterpreterBody::pop_container(CdlContainer old_container)
498
{
499
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_container");
500
    CYG_REPORT_FUNCARG2XV(this, old_container);
501
    CYG_PRECONDITION_THISC();
502
    CYG_PRECONDITION_ZERO_OR_CLASSC(old_container);
503
    CYG_PRECONDITIONC(0 != container);
504
 
505
    container = old_container;
506
 
507
    CYG_REPORT_RETURN();
508
}
509
 
510
CdlNode
511
CdlInterpreterBody::push_node(CdlNode new_node)
512
{
513
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_node", "result %p");
514
    CYG_REPORT_FUNCARG2XV(this, new_node);
515
    CYG_PRECONDITION_THISC();
516
    CYG_PRECONDITION_CLASSC(new_node);
517
 
518
    CdlNode result = node;
519
    node = new_node;
520
    CYG_REPORT_RETVAL(result);
521
    return result;
522
}
523
 
524
void
525
CdlInterpreterBody::pop_node(CdlNode old_node)
526
{
527
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_node");
528
    CYG_REPORT_FUNCARG2XV(this, old_node);
529
    CYG_PRECONDITION_THISC();
530
    CYG_PRECONDITIONC(0 != node);
531
    CYG_PRECONDITION_ZERO_OR_CLASSC(old_node);
532
 
533
    node = old_node;
534
 
535
    CYG_REPORT_RETURN();
536
}
537
 
538
std::string
539
CdlInterpreterBody::push_context(std::string new_context)
540
{
541
    CYG_REPORT_FUNCNAME("CdlInterpreter::push_context");
542
    CYG_REPORT_FUNCARG1XV(this);
543
    CYG_PRECONDITION_THISC();
544
    CYG_PRECONDITIONC("" != new_context);
545
 
546
    std::string result = context;
547
    context = new_context;
548
    return result;
549
}
550
 
551
void
552
CdlInterpreterBody::pop_context(std::string old_context)
553
{
554
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_context");
555
    CYG_REPORT_FUNCARG1XV(this);
556
    CYG_PRECONDITION_THISC();
557
    CYG_PRECONDITIONC("" != context);
558
 
559
    context = old_context;
560
 
561
    CYG_REPORT_RETURN();
562
}
563
 
564
CdlDiagnosticFnPtr
565
CdlInterpreterBody::push_error_fn_ptr(CdlDiagnosticFnPtr new_fn_ptr)
566
{
567
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_error_fn_ptr", "result %p");
568
    CYG_REPORT_FUNCARG2XV(this, new_fn_ptr);
569
    CYG_PRECONDITION_THISC();
570
    CYG_PRECONDITIONC(0 != new_fn_ptr);
571
 
572
    CdlDiagnosticFnPtr result = error_fn_ptr;
573
    error_fn_ptr = new_fn_ptr;
574
    CYG_REPORT_RETVAL(result);
575
    return result;
576
}
577
 
578
void
579
CdlInterpreterBody::pop_error_fn_ptr(CdlDiagnosticFnPtr old_fn_ptr)
580
{
581
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_error_fn_ptr");
582
    CYG_REPORT_FUNCARG2XV(this, old_fn_ptr);
583
    CYG_PRECONDITION_THISC();
584
    CYG_PRECONDITIONC(0 != error_fn_ptr);
585
 
586
    error_fn_ptr = old_fn_ptr;
587
 
588
    CYG_REPORT_RETURN();
589
}
590
 
591
CdlDiagnosticFnPtr
592
CdlInterpreterBody::push_warning_fn_ptr(CdlDiagnosticFnPtr new_fn_ptr)
593
{
594
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_warning_fn_ptr", "result %p");
595
    CYG_REPORT_FUNCARG2XV(this, new_fn_ptr);
596
    CYG_PRECONDITION_THISC();
597
    CYG_PRECONDITIONC(0 != new_fn_ptr);
598
 
599
    CdlDiagnosticFnPtr result = warning_fn_ptr;
600
    warning_fn_ptr = new_fn_ptr;
601
    CYG_REPORT_RETVAL(result);
602
    return result;
603
}
604
 
605
void
606
CdlInterpreterBody::pop_warning_fn_ptr(CdlDiagnosticFnPtr old_fn_ptr)
607
{
608
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_warning_fn_ptr");
609
    CYG_REPORT_FUNCARG2XV(this, old_fn_ptr);
610
    CYG_PRECONDITION_THISC();
611
    CYG_PRECONDITIONC(0 != warning_fn_ptr);
612
 
613
    warning_fn_ptr = old_fn_ptr;
614
 
615
    CYG_REPORT_RETURN();
616
}
617
 
618
//}}}
619
//{{{  CdlInterpreter:: get information                         
620
 
621
// ----------------------------------------------------------------------------
622
// Get hold of the underlying Tcl interpreter. This makes it easier to
623
// use miscellaneous Tcl library facilities such as Tcl_SplitList()
624
Tcl_Interp*
625
CdlInterpreterBody::get_tcl_interpreter(void) const
626
{
627
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_tcl_interpreter", "interpreter %p");
628
    CYG_REPORT_FUNCARG1XV(this);
629
    CYG_PRECONDITION_THISC();
630
 
631
    Tcl_Interp* result = tcl_interp;
632
    CYG_REPORT_RETVAL(result);
633
    return result;
634
}
635
 
636
//}}}
637
//{{{  CdlInterpreter:: eval()                                  
638
 
639
// ----------------------------------------------------------------------------
640
// Evaluate a Cdl script held in a string. The result of this evaluation, 
641
// e.g. TCL_OK, is returned directly. The string result is made available
642
// in an in-out parameter.
643
//
644
// According to the spec the underlying Tcl_Eval() routine needs to be able
645
// to make temporary changes to the script, so the latter must be held in
646
// writable memory. This requires a copy operation.
647
 
648
int
649
CdlInterpreterBody::eval(std::string script, std::string& str_result)
650
{
651
    CYG_REPORT_FUNCNAMETYPE("CdInterpreter::eval", "result %d");
652
    CYG_REPORT_FUNCARG1XV(this);
653
    CYG_PRECONDITION_THISC();
654
 
655
    int result  = TCL_OK;
656
    int size    = script.size();
657
 
658
    // Distinguish between results set by the Tcl interpreter and results
659
    // set by CDL-related commands running in that interpreter.
660
    cdl_result = false;
661
 
662
    if (size < 2048) {
663
        char buf[2048];
664
        script.copy(buf, size, 0);
665
        buf[size] = '\0';
666
        result = Tcl_Eval(tcl_interp, buf);
667
    } else {
668
        char* buf = static_cast<char*>(malloc(script.size() + 1));
669
        if (0 == buf) {
670
            this->set_result(CdlParse::construct_diagnostic(this, "internal error", "", "Out of memory"));
671
            result = TCL_ERROR;
672
        } else {
673
            script.copy(buf, size, 0);
674
            buf[size] = '\0';
675
            result = Tcl_Eval(tcl_interp, buf);
676
            free(buf);
677
        }
678
    }
679
 
680
    // The distinction between TCL_OK and TCL_RETURN is probably not worth
681
    // worrying about.
682
    if (TCL_RETURN == result) {
683
        result = TCL_OK;
684
    }
685
 
686
    // If we have an error condition that was raised by the Tcl
687
    // interpreter rather than by the library, it needs to be
688
    // raised up to the library level. That way the error count
689
    // etc. are kept accurate.
690
    if ((TCL_OK != result) && !cdl_result) {
691
        const char* tcl_result = Tcl_GetStringResult(tcl_interp);
692
        if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
693
            tcl_result = "Internal error, no additional information available.";
694
        }
695
        CdlParse::report_error(this, "", tcl_result);
696
    }
697
 
698
    str_result = Tcl_GetStringResult(tcl_interp);
699
    CYG_REPORT_RETVAL(result);
700
    return result;
701
}
702
 
703
// Ditto for Tcl Code that comes from a CDL file. Currently this is held
704
// as a string. In future it may be appropriate to store a byte-compiled
705
// version as well.
706
int
707
CdlInterpreterBody::eval_cdl_code(const cdl_tcl_code script, std::string& str_result)
708
{
709
    CYG_REPORT_FUNCNAMETYPE("CdInterpreter::eval_cdl_code", "result %d");
710
    CYG_REPORT_FUNCARG1XV(this);
711
    CYG_PRECONDITION_THISC();
712
 
713
    int result  = TCL_OK;
714
    int size    = script.size();
715
    // Distinguish between results set by the Tcl interpreter and results
716
    // set by CDL-related commands running in that interpreter.
717
    cdl_result = false;
718
 
719
    if (size < 2048) {
720
        char buf[2048];
721
        script.copy(buf, size, 0);
722
        buf[size] = '\0';
723
        result = Tcl_Eval(tcl_interp, buf);
724
    } else {
725
        char* buf = static_cast<char*>(malloc(script.size() + 1));
726
        if (0 == buf) {
727
            this->set_result(CdlParse::construct_diagnostic(this, "internal error", "", "Out of memory"));
728
            result = TCL_ERROR;
729
        } else {
730
            script.copy(buf, size, 0);
731
            buf[size] = '\0';
732
            result = Tcl_Eval(tcl_interp, buf);
733
            free(buf);
734
        }
735
    }
736
    // The distinction between TCL_OK and TCL_RETURN is probably not worth
737
    // worrying about.
738
    if (TCL_RETURN == result) {
739
        result = TCL_OK;
740
    }
741
 
742
    // If we have an error condition that was raised by the Tcl
743
    // interpreter rather than by the library, it needs to be
744
    // raised up to the library level. That way the error count
745
    // etc. are kept accurate.
746
    if ((TCL_OK != result) && !cdl_result) {
747
        const char* tcl_result = Tcl_GetStringResult(tcl_interp);
748
        if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
749
            tcl_result = "Internal error, no additional information available.";
750
        }
751
        CdlParse::report_error(this, "", tcl_result);
752
    }
753
 
754
    str_result = Tcl_GetStringResult(tcl_interp);
755
    CYG_REPORT_RETVAL(result);
756
    return result;
757
}
758
 
759
// Ditto for evaluating an entire file.
760
int
761
CdlInterpreterBody::eval_file(std::string script, std::string& str_result)
762
{
763
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::eval_file", "result %d");
764
    CYG_REPORT_FUNCARG1XV(this);
765
    CYG_PRECONDITION_THISC();
766
    CYG_PRECONDITIONC("" != script);
767
 
768
    // Distinguish between results set by the Tcl interpreter and results
769
    // set by CDL-related commands running in that interpreter.
770
    cdl_result = false;
771
 
772
    int result = Tcl_EvalFile(tcl_interp, CDL_TCL_CONST_CAST(char*, script.c_str()));
773
    // The distinction between TCL_OK and TCL_RETURN is probably not worth
774
    // worrying about.
775
    if (TCL_RETURN == result) {
776
        result = TCL_OK;
777
    }
778
 
779
    // If we have an error condition that was raised by the Tcl
780
    // interpreter rather than by the library, it needs to be
781
    // raised up to the library level. That way the error count
782
    // etc. are kept accurate.
783
    if ((TCL_OK != result) && !cdl_result) {
784
        const char* tcl_result = Tcl_GetStringResult(tcl_interp);
785
        if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
786
            tcl_result = "Internal error, no additional information available.";
787
        }
788
        CdlParse::report_error(this, "", tcl_result);
789
    }
790
 
791
    str_result = Tcl_GetStringResult(tcl_interp);
792
    CYG_REPORT_RETVAL(result);
793
    return result;
794
}
795
 
796
// Variants for when the result string is of no interest
797
int
798
CdlInterpreterBody::eval(std::string script)
799
{
800
    std::string result_string;
801
    return this->eval(script, result_string);
802
}
803
 
804
int
805
CdlInterpreterBody::eval_cdl_code(const cdl_tcl_code script)
806
{
807
    std::string result_string;
808
    return this->eval_cdl_code(script, result_string);
809
}
810
 
811
int
812
CdlInterpreterBody::eval_file(std::string filename)
813
{
814
    std::string result_string;
815
    return this->eval_file(filename, result_string);
816
}
817
 
818
//}}}
819
//{{{  CdlInterpreter:: set_result()                            
820
 
821
// ----------------------------------------------------------------------------
822
// Provide a way of setting an interpreter's result from a command implemented
823
// in C++.
824
 
825
void
826
CdlInterpreterBody::set_result(std::string result)
827
{
828
    CYG_REPORT_FUNCNAME("CdlInterpreter::set_result");
829
    CYG_PRECONDITION_THISC();
830
 
831
    Tcl_SetResult(tcl_interp, const_cast<char*>(result.c_str()), TCL_VOLATILE);
832
    this->cdl_result = true;
833
 
834
    CYG_REPORT_RETURN();
835
}
836
 
837
bool
838
CdlInterpreterBody::result_set_by_cdl()
839
{
840
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::result_set_by_cdl", "result %d");
841
    CYG_PRECONDITION_THISC();
842
 
843
    bool result = this->cdl_result;
844
    CYG_REPORT_RETVAL(result);
845
    return result;
846
}
847
 
848
// ----------------------------------------------------------------------------
849
// Also allow the result to be extracted again.
850
std::string
851
CdlInterpreterBody::get_result()
852
{
853
    CYG_REPORT_FUNCNAME("CdlInterpreter::get_result");
854
    CYG_PRECONDITION_THISC();
855
 
856
    std::string result = Tcl_GetStringResult(tcl_interp);
857
 
858
    CYG_REPORT_RETURN();
859
    return result;
860
}
861
 
862
//}}}
863
//{{{  CdlInterpreter:: add and remove commands                 
864
 
865
// ----------------------------------------------------------------------------
866
// This is the Tcl command proc that gets used for all CdlInterpreter
867
// commands. The ClientData field will be a CdlInterpreterCommand,
868
// i.e. a function pointer. That function needs a pointer to the
869
// CdlInterpreter object, which can be accessed via AssocData.
870
int
871
CdlInterpreterBody::tcl_command_proc(ClientData data, Tcl_Interp* tcl_interp, int argc, const char* argv[])
872
{
873
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::tcl_command_proc", "result %d");
874
    CYG_REPORT_FUNCARG3XV(data, tcl_interp, argc);
875
    CYG_PRECONDITIONC(0 != data);
876
 
877
    int result = TCL_OK;
878
 
879
    union {
880
        ClientData            data;
881
        CdlInterpreterCommand command;
882
    } x;
883
    x.data = data;
884
    CdlInterpreterCommand command = x.command;
885
 
886
    data = Tcl_GetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0);
887
    CdlInterpreter interp = static_cast<CdlInterpreter>(data);
888
    CYG_ASSERT_CLASSC(interp);
889
 
890
    try {
891
        result = (*command)(interp, argc, argv);
892
    } catch(std::bad_alloc e) {
893
        interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Out of memory."));
894
        result = TCL_ERROR;
895
    } catch(CdlStringException e) {
896
        interp->set_result(e.get_message());
897
        result = TCL_ERROR;
898
    } catch(...) {
899
        CYG_FAIL("Unexpected C++ exception");
900
        interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Unexpected C++ exception."));
901
        result = TCL_ERROR;
902
    }
903
 
904
    CYG_REPORT_RETVAL(result);
905
    return result;
906
}
907
 
908
void
909
CdlInterpreterBody::add_command(std::string name, CdlInterpreterCommand command)
910
{
911
    CYG_REPORT_FUNCNAME("CdlInterpreter::add_command");
912
    CYG_REPORT_FUNCARG2XV(this, command);
913
 
914
    CYG_PRECONDITION_THISC();
915
    CYG_PRECONDITIONC("" != name);
916
    CYG_CHECK_FUNC_PTRC(command);
917
 
918
    union {
919
        CdlInterpreterCommand command;
920
        ClientData            data;
921
    } x;
922
    x.command = command;
923
 
924
    // Tcl 8.4 involves some incompatible API changes
925
#if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))
926
    if (0 == Tcl_CreateCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), &tcl_command_proc, x.data, 0)) {
927
        throw std::bad_alloc();
928
    }
929
#else
930
    if (0 == Tcl_CreateCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()),
931
                               (int (*)(ClientData,Tcl_Interp*, int, char*[])) &tcl_command_proc,
932
                               x.data, 0)) {
933
        throw std::bad_alloc();
934
    }
935
#endif
936
 
937
    CYG_REPORT_RETURN();
938
}
939
 
940
// ----------------------------------------------------------------------------
941
// Remove a command from an interpreter. This is just a wrapper for the
942
// Tcl_DeleteCommand() routine.
943
 
944
void
945
CdlInterpreterBody::remove_command(std::string name)
946
{
947
    CYG_REPORT_FUNCNAME("CdlInterpreter::remove_command");
948
    CYG_REPORT_FUNCARG1XV(this);
949
    CYG_PRECONDITION_THISC();
950
    CYG_PRECONDITIONC("" != name);
951
 
952
    if (0 != Tcl_DeleteCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()))) {
953
        CYG_FAIL("attempt to delete non-existant command");
954
    }
955
    CYG_REPORT_RETURN();
956
}
957
 
958
// ----------------------------------------------------------------------------
959
// It is also possible to add and remove whole sets of commands in one go,
960
// keeping track of the current set.
961
 
962
std::vector<CdlInterpreterCommandEntry>*
963
CdlInterpreterBody::push_commands(std::vector<CdlInterpreterCommandEntry>& new_commands)
964
{
965
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_commands", "result %p");
966
    CYG_REPORT_FUNCARG2XV(this, &new_commands);
967
    CYG_PRECONDITION_THISC();
968
 
969
    std::vector<CdlInterpreterCommandEntry>* result = current_commands;
970
    std::vector<CdlInterpreterCommandEntry>::iterator i;
971
 
972
    // First uninstall all the old commands, if any
973
    if (0 != current_commands) {
974
        for (i = current_commands->begin(); i != current_commands->end(); i++) {
975
            remove_command(i->name);
976
        }
977
    }
978
 
979
    // Now install the new commands
980
    for (i = new_commands.begin(); i != new_commands.end(); i++) {
981
        add_command(i->name, i->command);
982
    }
983
 
984
    // Remember the current set in case of a subsequent push operation
985
    current_commands = &new_commands;
986
 
987
    CYG_REPORT_RETVAL(result);
988
    return result;
989
}
990
 
991
void
992
CdlInterpreterBody::pop_commands(std::vector<CdlInterpreterCommandEntry>* original_commands)
993
{
994
    CYG_REPORT_FUNCNAME("CdlInterpreter::pop_commands");
995
    CYG_REPORT_FUNCARG2XV(this, &original_commands);
996
    CYG_PRECONDITION_THISC();
997
    CYG_PRECONDITION(0 != current_commands, "no pop without a previous push please");
998
 
999
    std::vector<CdlInterpreterCommandEntry>::iterator i;
1000
    // Uninstall the most recent set of commands
1001
    for (i = current_commands->begin(); i != current_commands->end(); i++) {
1002
        remove_command(i->name);
1003
    }
1004
 
1005
    // Reinstall the previous set, if any
1006
    if (0 != original_commands) {
1007
        for (i = original_commands->begin(); i != original_commands->end(); i++) {
1008
            add_command(i->name, i->command);
1009
        }
1010
    }
1011
    current_commands = original_commands;
1012
    CYG_REPORT_RETURN();
1013
}
1014
 
1015
std::vector<CdlInterpreterCommandEntry>*
1016
CdlInterpreterBody::get_pushed_commands() const
1017
{
1018
    CYG_REPORT_FUNCNAME("CdlInterpreter::get_pushed_commands");
1019
    CYG_REPORT_FUNCARG1XV(this);
1020
    CYG_PRECONDITION_THISC();
1021
 
1022
    CYG_REPORT_RETURN();
1023
    return current_commands;
1024
}
1025
 
1026
//}}}
1027
//{{{  CdlInterpreter:: variables                               
1028
 
1029
// ----------------------------------------------------------------------------
1030
// Provide some more stubs, this time for accessing Tcl global variables.
1031
void
1032
CdlInterpreterBody::set_variable(std::string name, std::string value)
1033
{
1034
    CYG_REPORT_FUNCNAME("CdlInterpreter::set_variable");
1035
    CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1036
    CYG_PRECONDITION_THISC();
1037
    CYG_PRECONDITIONC("" != name);
1038
    if (0 == Tcl_SetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), CDL_TCL_CONST_CAST(char*, value.c_str()), TCL_GLOBAL_ONLY)) {
1039
        throw std::bad_alloc();
1040
    }
1041
    CYG_REPORT_RETURN();
1042
}
1043
 
1044
void
1045
CdlInterpreterBody::unset_variable(std::string name)
1046
{
1047
    CYG_REPORT_FUNCNAME("CdlInterpreter::unset_variable");
1048
    CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1049
    CYG_PRECONDITION_THISC();
1050
    CYG_PRECONDITIONC("" != name);
1051
 
1052
    Tcl_UnsetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), TCL_GLOBAL_ONLY);
1053
    CYG_REPORT_RETURN();
1054
}
1055
 
1056
std::string
1057
CdlInterpreterBody::get_variable(std::string name)
1058
{
1059
    CYG_REPORT_FUNCNAME("CdlInterpreter::get_variable");
1060
    CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1061
    CYG_PRECONDITION_THISC();
1062
    CYG_PRECONDITIONC("" != name);
1063
 
1064
    std::string result = "";
1065
    const char *tmp = Tcl_GetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), TCL_GLOBAL_ONLY);
1066
    if (0 != tmp) {
1067
        result = tmp;
1068
    }
1069
 
1070
    CYG_REPORT_RETURN();
1071
    return result;
1072
}
1073
 
1074
//}}}
1075
//{{{  CdlInterpreter:: assoc data                              
1076
 
1077
// ----------------------------------------------------------------------------
1078
// Associated data. It is useful to be able to store some C++ data with
1079
// Tcl interpreters, so that the implementations of various commands
1080
// can retrieve details of the current state. Tcl provides the necessary
1081
// underlying support via routines Tcl_SetAssocData() etc., and the
1082
// routines here are just stubs for the underlying Tcl ones.
1083
 
1084
void
1085
CdlInterpreterBody::set_assoc_data(const char* key, ClientData data, Tcl_InterpDeleteProc* del_proc)
1086
{
1087
    CYG_REPORT_FUNCNAME("CdlInterpreter::set_assoc_data");
1088
    CYG_REPORT_FUNCARG3("this %p, key %s, data %p", this, key, data);
1089
    CYG_PRECONDITION_THISC();
1090
    CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1091
 
1092
    Tcl_SetAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key), del_proc, data);
1093
    CYG_REPORT_RETURN();
1094
}
1095
 
1096
ClientData
1097
CdlInterpreterBody::get_assoc_data(const char* key)
1098
{
1099
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_assoc_data", "result %p");
1100
    CYG_REPORT_FUNCARG2("this %p, key %s", this, key);
1101
    CYG_PRECONDITION_THISC();
1102
    CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1103
 
1104
    ClientData result = Tcl_GetAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key), 0);
1105
    CYG_REPORT_RETVAL(result);
1106
    return result;
1107
}
1108
 
1109
void
1110
CdlInterpreterBody::delete_assoc_data(const char* key)
1111
{
1112
    CYG_REPORT_FUNCNAME("CdlInterpreter::delete_assoc_data");
1113
    CYG_REPORT_FUNCARG2("this %p, key %s", this, key);
1114
    CYG_PRECONDITION_THISC();
1115
    CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1116
 
1117
    Tcl_DeleteAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key));
1118
    CYG_REPORT_RETURN();
1119
}
1120
 
1121
//}}}
1122
//{{{  CdlInterpreter:: file I/O                                
1123
 
1124
// ----------------------------------------------------------------------------
1125
// Tcl provides file I/O facilities that are already known to be portable
1126
// to the platforms of interest.
1127
 
1128
bool
1129
CdlInterpreterBody::is_directory(std::string name)
1130
{
1131
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::is_directory", "result %d");
1132
    CYG_REPORT_FUNCARG1XV(this);
1133
    CYG_PRECONDITION_THISC();
1134
    CYG_PRECONDITIONC("" != name);
1135
 
1136
    bool result = false;
1137
    std::string command = "file isdirectory \"" + name + "\"";
1138
    std::string tcl_result;
1139
    if ((TCL_OK == this->eval(command, tcl_result)) && ("1" == tcl_result)) {
1140
        result = true;
1141
    }
1142
 
1143
    CYG_REPORT_RETVAL(result);
1144
    return result;
1145
}
1146
 
1147
bool
1148
CdlInterpreterBody::is_file(std::string name)
1149
{
1150
    CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::is_file", "result %d");
1151
    CYG_REPORT_FUNCARG1XV(this);
1152
    CYG_PRECONDITION_THISC();
1153
    CYG_PRECONDITIONC("" != name);
1154
 
1155
    bool result = false;
1156
    std::string command = "file isfile \"" + name + "\"";
1157
    std::string tcl_result;
1158
    if ((TCL_OK == this->eval(command, tcl_result)) && ("1" == tcl_result)) {
1159
        result = true;
1160
    }
1161
 
1162
    CYG_REPORT_RETVAL(result);
1163
    return result;
1164
}
1165
 
1166
// ----------------------------------------------------------------------------
1167
 
1168
void
1169
CdlInterpreterBody::locate_subdirs(std::string directory, std::vector<std::string>& result)
1170
{
1171
    CYG_REPORT_FUNCNAME("CdlInterpreter::locate_subdirs");
1172
    CYG_REPORT_FUNCARG2XV(this, &result);
1173
    CYG_PRECONDITION_THISC();
1174
 
1175
    static char locate_subdirs_script[] = "\
1176
set pattern [file join \"$::cdl_locate_subdirs_path\" * .]  \n\
1177
set result {}                                               \n\
1178
foreach entry [glob -nocomplain -- $pattern] {              \n\
1179
    set entry [file tail [file dirname $entry]]             \n\
1180
    if {($entry != \"CVS\") && ($entry != \"cvs\")} {       \n\
1181
        lappend result $entry                               \n\
1182
    }                                                       \n\
1183
}                                                           \n\
1184
return $result                                              \n\
1185
";
1186
 
1187
    std::string                 tcl_result = "";
1188
    set_variable("::cdl_locate_subdirs_path", directory);
1189
    if (TCL_OK != eval(locate_subdirs_script, tcl_result)) {
1190
        CYG_FAIL("Internal error evaluating Tcl script");
1191
    }
1192
 
1193
    int             count;
1194
    const char**    array;
1195
    if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, tcl_result.c_str()), &count, CDL_TCL_CONST_CAST(char***, &array))) {
1196
        throw std::bad_alloc();
1197
    }
1198
    for (int i = 0; i < count; i++) {
1199
        result.push_back(array[i]);
1200
    }
1201
    Tcl_Free((char*) array);
1202
 
1203
    CYG_REPORT_RETURN();
1204
}
1205
 
1206
// ----------------------------------------------------------------------------
1207
// Locating all subdirs requires some simple recursion
1208
void
1209
CdlInterpreterBody::locate_all_subdirs(std::string directory, std::vector<std::string>& result)
1210
{
1211
    CYG_REPORT_FUNCNAME("CdlInterpreter::locate_all_subdirs");
1212
    CYG_REPORT_FUNCARG2XV(this, &result);
1213
    CYG_PRECONDITION_THISC();
1214
    CYG_PRECONDITIONC("" != directory);
1215
 
1216
    std::vector<std::string> subdirs;
1217
    locate_subdirs(directory, subdirs);
1218
    std::vector<std::string>::const_iterator i, j;
1219
 
1220
    for (i = subdirs.begin(); i != subdirs.end(); i++) {
1221
        result.push_back(*i);
1222
        std::vector<std::string> its_subdirs;
1223
        locate_all_subdirs(directory + "/" + *i, its_subdirs);
1224
        for (j = its_subdirs.begin(); j != its_subdirs.end(); j++) {
1225
            result.push_back(*i + "/" + *j);
1226
        }
1227
    }
1228
 
1229
    CYG_REPORT_RETURN();
1230
}
1231
 
1232
// ----------------------------------------------------------------------------
1233
// Locating the files in a particular subdirectory. This requires another
1234
// simple Tcl script.
1235
void
1236
CdlInterpreterBody::locate_files(std::string directory, std::vector<std::string>& result)
1237
{
1238
    CYG_REPORT_FUNCNAME("CdlInterpreter::locate_files");
1239
    CYG_REPORT_FUNCARG2XV(this, &result);
1240
    CYG_PRECONDITION_THISC();
1241
    CYG_PRECONDITIONC("" != directory);
1242
 
1243
    static char locate_files_script[] = "\
1244
set pattern [file join \"$::cdl_locate_files_path\" *]  \n\
1245
set result {}                                           \n\
1246
foreach entry [glob -nocomplain -- $pattern] {          \n\
1247
    if ([file isdirectory $entry]) {                    \n\
1248
        continue                                        \n\
1249
    }                                                   \n\
1250
    lappend result [file tail $entry]                   \n\
1251
 }                                                      \n\
1252
return $result                                          \n\
1253
";
1254
 
1255
    std::string                 tcl_result;
1256
    set_variable("::cdl_locate_files_path", directory);
1257
    if (TCL_OK != eval(locate_files_script, tcl_result)) {
1258
        CYG_FAIL("Internal error evaluating Tcl script");
1259
    }
1260
    int             count;
1261
    const char**    array;
1262
    if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, tcl_result.c_str()), &count, CDL_TCL_CONST_CAST(char***, &array))) {
1263
        throw std::bad_alloc();
1264
    }
1265
    for (int i = 0; i < count; i++) {
1266
        result.push_back(array[i]);
1267
    }
1268
    Tcl_Free((char*) array);
1269
 
1270
    CYG_REPORT_RETURN();
1271
}
1272
 
1273
// ----------------------------------------------------------------------------
1274
// Locating all files can be achieved by combining locate_all_subdirs()
1275
// and locate_files().
1276
void
1277
CdlInterpreterBody::locate_all_files(std::string directory, std::vector<std::string>& result)
1278
{
1279
    CYG_REPORT_FUNCNAME("CdlInterpreter::locate_all_files");
1280
    CYG_REPORT_FUNCARG2XV(this, &result);
1281
    CYG_PRECONDITION_THISC();
1282
    CYG_PRECONDITIONC("" != directory);
1283
 
1284
    std::vector<std::string> files;
1285
    std::vector<std::string>::const_iterator i, j;
1286
    locate_files(directory, files);
1287
    for (i = files.begin(); i != files.end(); i++) {
1288
        result.push_back(*i);
1289
    }
1290
 
1291
    std::vector<std::string> all_subdirs;
1292
    locate_all_subdirs(directory, all_subdirs);
1293
    for (i = all_subdirs.begin(); i != all_subdirs.end(); i++) {
1294
        std::vector<std::string> subdir_files;
1295
        locate_files(directory + "/" + *i, subdir_files);
1296
        for (j = subdir_files.begin(); j != subdir_files.end(); j++) {
1297
            result.push_back(*i + "/" + *j);
1298
        }
1299
    }
1300
 
1301
    CYG_REPORT_RETURN();
1302
}
1303
 
1304
// ----------------------------------------------------------------------------
1305
// Write some data to a file, throwing an I/O exception on failure. This
1306
// functionality is needed whenever savefile data is generated, it is
1307
// convenient to have a utility function to do the job. Also, performing
1308
// the Tcl_Write involves passing const data as a non-const argument:
1309
// if this ever causes problems in future it is a good idea to isolate
1310
// the problem here.
1311
 
1312
void
1313
CdlInterpreterBody::write_data(Tcl_Channel chan, std::string data)
1314
{
1315
    CYG_REPORT_FUNCNAME("CdlInterpreter::write_data");
1316
    CYG_REPORT_FUNCARG2XV(this, chan);
1317
    CYG_PRECONDITION_THISC();
1318
 
1319
    if (-1 == Tcl_Write(chan, CDL_TCL_CONST_CAST(char*, data.data()), data.size())) {
1320
        std::string msg = "Unexpected error writing to file " + this->get_context() + " : " + Tcl_PosixError(tcl_interp);
1321
        throw CdlInputOutputException(msg);
1322
    }
1323
 
1324
    CYG_REPORT_RETURN();
1325
}
1326
 
1327
//}}}
1328
//{{{  CdlInterpreter:: quote() etc.                            
1329
 
1330
// ----------------------------------------------------------------------------
1331
// Given a string, quote it in such a way that the Tcl interpreter will
1332
// process it as a single word, but keep the result as human-readable
1333
// as possible. If there are no special characters then just return the
1334
// string itself. Otherwise quoting is necessary.
1335
//
1336
// The choice is between braces and double quotes. Generally braces
1337
// are better and more consistent, but there is a problem if the
1338
// string argument itself contains braces. These could be
1339
// backslash-escaped, but the Tcl interpreter will not automatically
1340
// remove the backslashes so we would end up with a discrepancy
1341
// between the original data and what is seen by the parser. In this
1342
// case quote marks have to be used instead.
1343
//
1344
// NOTE: this code may not behave sensibly when it comes to i18n
1345
// issues.
1346
 
1347
std::string
1348
CdlInterpreterBody::quote(std::string src)
1349
{
1350
    CYG_REPORT_FUNCNAME("CdlInterpreter::quote");
1351
 
1352
    std::string  result = "";
1353
    bool         contains_specials = false;
1354
    unsigned int i;
1355
 
1356
    if (0 == src.size()) {
1357
        // An empty string. The best way to represent this is an empty
1358
        // set of quotes.
1359
        result = "\"\"";
1360
        CYG_REPORT_RETURN();
1361
        return result;
1362
    }
1363
 
1364
    if ('#' == src[0]) {
1365
        contains_specials = true;
1366
    }
1367
 
1368
    for (i = 0; (i < src.size()) && !contains_specials; i++) {
1369
        if (isspace(src[i])) {
1370
            contains_specials = true;
1371
            break;
1372
        }
1373
        switch(src[i]) {
1374
          case '{':
1375
          case '}':
1376
          case '\\':
1377
          case '$':
1378
          case '"':
1379
          case '[':
1380
          case ']':
1381
          case '#':
1382
          case ';':
1383
              contains_specials = true;
1384
              break;
1385
 
1386
          default:
1387
              break;
1388
        }
1389
    }
1390
 
1391
    if (!contains_specials) {
1392
        result = src;
1393
    } else{
1394
        // If the data is a multiline item, it is better to start it in column 0.
1395
        // Unfortunately there is the question of what to do with the opening
1396
        // quote. Putting it on the current line, followed by a backslash-escaped
1397
        // newline, introduces a space into the string. If the string begins with
1398
        // a space anyway then arguably this would be harmless, but it could
1399
        // be confusing to the user. Putting the opening double quote into column 0
1400
        // means that the first line of data is indented relative to the rest of
1401
        // the data, but still appears to be the best alternative.
1402
        if (src.find('\n') != std::string::npos) {
1403
            result += "\\\n";
1404
        }
1405
        result += '\"';
1406
        for (i = 0; i < src.size(); i++) {
1407
            switch(src[i]) {
1408
              case '\\':
1409
              case '$':
1410
              case '"':
1411
              case '[':
1412
              case ']':
1413
                  result += '\\';
1414
                  result += src[i];
1415
                  break;
1416
 
1417
              default:
1418
                result += src[i];
1419
                break;
1420
            }
1421
        }
1422
        result += '\"';
1423
    }
1424
 
1425
    CYG_REPORT_RETURN();
1426
    return result;
1427
}
1428
 
1429
// ----------------------------------------------------------------------------
1430
// Given some data which may be multiline, return a string which corresponds
1431
// to that data turned into a comment.
1432
 
1433
std::string
1434
CdlInterpreterBody::multiline_comment(const std::string& orig, int first_indent, int second_indent)
1435
{
1436
    CYG_REPORT_FUNCNAME("CdlInterpreter::multiline_comment");
1437
 
1438
    std::string indent  = std::string(first_indent, ' ') + "# " + std::string(second_indent, ' ');
1439
    std::string result  = "";
1440
    bool indent_needed = true;
1441
 
1442
    std::string::const_iterator str_i;
1443
    for (str_i = orig.begin(); str_i != orig.end(); str_i++) {
1444
        if (indent_needed) {
1445
            result += indent;
1446
            indent_needed = false;
1447
        }
1448
        result += *str_i;
1449
        if ('\n' == *str_i) {
1450
            indent_needed = true;
1451
        }
1452
    }
1453
 
1454
    CYG_REPORT_RETURN();
1455
    return result;
1456
}
1457
 
1458
// ----------------------------------------------------------------------------
1459
// Given some data, append it to the current line and add additional commented
1460
// and indented lines as required.
1461
std::string
1462
CdlInterpreterBody::extend_comment(const std::string& orig, int first_indent, int second_indent)
1463
{
1464
    CYG_REPORT_FUNCNAME("CdlInterpreter::extend_comment");
1465
 
1466
    std::string indent  = std::string(first_indent, ' ') + "# " + std::string(second_indent, ' ');
1467
    std::string result = "";
1468
    bool indent_needed = false;
1469
 
1470
    std::string::const_iterator str_i;
1471
    for (str_i = orig.begin(); str_i != orig.end(); str_i++) {
1472
        if (indent_needed) {
1473
            result += indent;
1474
            indent_needed = false;
1475
        }
1476
        result += *str_i;
1477
        if ('\n' == *str_i) {
1478
            indent_needed = true;
1479
        }
1480
    }
1481
 
1482
    CYG_REPORT_RETURN();
1483
    return result;
1484
}
1485
 
1486
//}}}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.