Expose SGPropertyNode::removeAllChildren() to Nasal.
[fg:hoorays-flightgear.git] / src / Scripting / nasal-props.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include "config.h"
3 #endif
4
5 #include <cstring>
6
7 #include <simgear/nasal/nasal.h>
8 #include <simgear/props/props.hxx>
9 #include <simgear/props/vectorPropTemplates.hxx>
10
11 #include <Main/globals.hxx>
12
13 #include "NasalSys.hxx"
14
15 using namespace std;
16
17 // Implementation of a Nasal wrapper for the SGPropertyNode class,
18 // using the Nasal "ghost" (er... Garbage collection Handle for
19 // OutSide Thingy) facility.
20 //
21 // Note that these functions appear in Nasal with prepended
22 // underscores.  They work on the low-level "ghost" objects and aren't
23 // intended for use from user code, but from Nasal code you will find
24 // in props.nas.  That is where the Nasal props.Node class is defined,
25 // which provides a saner interface along the lines of SGPropertyNode.
26
27 static void propNodeGhostDestroy(void* ghost)
28 {
29     SGPropertyNode_ptr* prop = (SGPropertyNode_ptr*)ghost;
30     delete prop;
31 }
32
33 naGhostType PropNodeGhostType = { propNodeGhostDestroy, "prop" };
34
35 naRef propNodeGhostCreate(naContext c, SGPropertyNode* n)
36 {
37     if(!n) return naNil();
38     SGPropertyNode_ptr* ghost = new SGPropertyNode_ptr(n);
39     return naNewGhost(c, &PropNodeGhostType, ghost);
40 }
41
42 naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
43 {
44     return propNodeGhostCreate(_context, handle);
45 }
46
47 SGPropertyNode* ghostToPropNode(naRef ref)
48 {
49   if (!naIsGhost(ref) || (naGhost_type(ref) != &PropNodeGhostType))
50     return NULL;
51
52   SGPropertyNode_ptr* pp = (SGPropertyNode_ptr*) naGhost_ptr(ref);
53   return pp->ptr();
54 }
55
56 #define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
57
58 //
59 // Standard header for the extension functions.  It turns the "ghost"
60 // found in arg[0] into a SGPropertyNode_ptr*, and then "unwraps" the
61 // vector found in the second argument into a normal-looking args
62 // array.  This allows the Nasal handlers to do things like:
63 //   Node.getChild = func { _getChild(me.ghost, arg) }
64 //
65 #define NODENOARG()                                                            \
66     if(argc < 2 || !naIsGhost(args[0]) ||                                      \
67         naGhost_type(args[0]) != &PropNodeGhostType)                           \
68         naRuntimeError(c, "bad argument to props function");                   \
69     SGPropertyNode_ptr node = *(SGPropertyNode_ptr*)naGhost_ptr(args[0]);
70
71 #define NODEARG()                                                              \
72     NODENOARG();                                                               \
73     naRef argv = args[1]
74
75 //
76 // Pops the first argument as a relative path if the first condition
77 // is true (e.g. argc > 1 for getAttribute) and if it is a string.
78 // If the second confition is true, then another is popped to specify
79 // if the node should be created (i.e. like the second argument to
80 // getNode())
81 //
82 // Note that this makes the function return nil if the node doesn't
83 // exist, so all functions with a relative_path parameter will
84 // return nil if the specified node does not exist.
85 //
86 #define MOVETARGET(cond1, create)                                              \
87     if(cond1) {                                                                \
88         naRef name = naVec_get(argv, 0);                                       \
89         if(naIsString(name)) {                                                 \
90             try {                                                              \
91                 node = node->getNode(naStr_data(name), create);                \
92             } catch(const string& err) {                                       \
93                 naRuntimeError(c, (char *)err.c_str());                        \
94                 return naNil();                                                \
95             }                                                                  \
96             if(!node) return naNil();                                          \
97             naVec_removefirst(argv); /* pop only if we were successful */      \
98         }                                                                      \
99     }
100
101
102 // Get the type of a property (returns a string).
103 // Forms:
104 //    props.Node.getType(string relative_path);
105 //    props.Node.getType();
106 static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
107 {
108     using namespace simgear;
109     NODEARG();
110     MOVETARGET(naVec_size(argv) > 0, false);
111     const char* t = "unknown";
112     switch(node->getType()) {
113     case props::NONE:   t = "NONE";   break;
114     case props::ALIAS:  t = "ALIAS";  break;
115     case props::BOOL:   t = "BOOL";   break;
116     case props::INT:    t = "INT";    break;
117     case props::LONG:   t = "LONG";   break;
118     case props::FLOAT:  t = "FLOAT";  break;
119     case props::DOUBLE: t = "DOUBLE"; break;
120     case props::STRING: t = "STRING"; break;
121     case props::UNSPECIFIED: t = "UNSPECIFIED"; break;
122     case props::VEC3D:  t = "VEC3D";  break;
123     case props::VEC4D:  t = "VEC4D";  break;
124     case props::EXTENDED: t = "EXTENDED";  break; // shouldn't happen
125     }
126     return NASTR(t);
127 }
128
129
130 // Get an attribute of a property by name (returns true/false).
131 // Forms:
132 //    props.Node.getType(string relative_path,
133 //                       string attribute_name);
134 //    props.Node.getType(string attribute_name);
135 static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
136 {
137     NODEARG();
138     if(naVec_size(argv) == 0) return naNum(unsigned(node->getAttributes()));
139     MOVETARGET(naVec_size(argv) > 1, false);
140     naRef val = naVec_get(argv, 0);
141     const char *a = naStr_data(val);
142     SGPropertyNode::Attribute attr;
143     if(!a) a = "";
144     if(!strcmp(a, "last")) return naNum(SGPropertyNode::LAST_USED_ATTRIBUTE);
145     else if(!strcmp(a, "children"))    return naNum(node->nChildren());
146     else if(!strcmp(a, "listeners"))   return naNum(node->nListeners());
147     // Number of references without instance used in this function
148     else if(!strcmp(a, "references"))  return naNum(node.getNumRefs() - 1);
149     else if(!strcmp(a, "tied"))        return naNum(node->isTied());
150     else if(!strcmp(a, "alias"))       return naNum(node->isAlias());
151     else if(!strcmp(a, "readable"))    attr = SGPropertyNode::READ;
152     else if(!strcmp(a, "writable"))    attr = SGPropertyNode::WRITE;
153     else if(!strcmp(a, "archive"))     attr = SGPropertyNode::ARCHIVE;
154     else if(!strcmp(a, "trace-read"))  attr = SGPropertyNode::TRACE_READ;
155     else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
156     else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
157     else if(!strcmp(a, "preserve"))    attr = SGPropertyNode::PRESERVE;
158     else {
159         naRuntimeError(c, "props.getAttribute() with invalid attribute");
160         return naNil();
161     }
162     return naNum(node->getAttribute(attr));
163 }
164
165
166 // Set an attribute by name and boolean value or raw (bitmasked) number.
167 // Forms:
168 //    props.Node.setAttribute(string relative_path,
169 //                            string attribute_name,
170 //                            bool value);
171 //    props.Node.setAttribute(string attribute_name,
172 //                            bool value);
173 //    props.Node.setArtribute(int attributes);
174 static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
175 {
176     NODEARG();
177     MOVETARGET(naVec_size(argv) > 2, false);
178     naRef val = naVec_get(argv, 0);
179     if(naVec_size(argv) == 1 && naIsNum(val)) {
180         naRef ret = naNum(node->getAttributes());
181         node->setAttributes((int)val.num);
182         return ret;
183     }
184     SGPropertyNode::Attribute attr;
185     const char *a = naStr_data(val);
186     if(!a) a = "";
187     if(!strcmp(a, "readable"))         attr = SGPropertyNode::READ;
188     else if(!strcmp(a, "writable"))    attr = SGPropertyNode::WRITE;
189     else if(!strcmp(a, "archive"))     attr = SGPropertyNode::ARCHIVE;
190     else if(!strcmp(a, "trace-read"))  attr = SGPropertyNode::TRACE_READ;
191     else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
192     else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
193     else if(!strcmp(a, "preserve"))    attr = SGPropertyNode::PRESERVE;
194     else {
195         naRuntimeError(c, "props.setAttribute() with invalid attribute");
196         return naNil();
197     }
198     naRef ret = naNum(node->getAttribute(attr));
199     node->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
200     return ret;
201 }
202
203
204 // Get the simple name of this node.
205 // Forms:
206 //    props.Node.getName();
207 static naRef f_getName(naContext c, naRef me, int argc, naRef* args)
208 {
209     NODENOARG();
210     return NASTR(node->getName());
211 }
212
213
214 // Get the index of this node.
215 // Forms:
216 //    props.Node.getIndex();
217 static naRef f_getIndex(naContext c, naRef me, int argc, naRef* args)
218 {
219     NODENOARG();
220     return naNum(node->getIndex());
221 }
222
223 // Check if other_node refers to the same as this node.
224 // Forms:
225 //    props.Node.equals(other_node);
226 static naRef f_equals(naContext c, naRef me, int argc, naRef* args)
227 {
228     NODEARG();
229
230     naRef rhs = naVec_get(argv, 0);
231     if( !naIsGhost(rhs) || naGhost_type(rhs) != &PropNodeGhostType )
232       return naNum(false);
233
234     SGPropertyNode_ptr node_rhs = *(SGPropertyNode_ptr*)naGhost_ptr(rhs);
235     return naNum(node == node_rhs);
236 }
237
238 template<typename T>
239 naRef makeVectorFromVec(naContext c, const T& vec)
240 {
241     const int num_components
242         = sizeof(vec.data()) / sizeof(typename T::value_type);
243     naRef vector = naNewVector(c);
244     naVec_setsize(c, vector, num_components);
245     for (int i = 0; i < num_components; ++i)
246         naVec_set(vector, i, naNum(vec[i]));
247     return vector;
248 }
249
250
251 // Get the value of a node, with or without a relative path.
252 // Forms:
253 //    props.Node.getValue(string relative_path);
254 //    props.Node.getValue();
255 static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
256 {
257     using namespace simgear;
258     NODEARG();
259     MOVETARGET(naVec_size(argv) > 0, false);
260     switch(node->getType()) {
261     case props::BOOL:   case props::INT:
262     case props::LONG:   case props::FLOAT:
263     case props::DOUBLE:
264     {
265         double dv = node->getDoubleValue();
266         if (SGMisc<double>::isNaN(dv)) {
267           SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
268           return naNil();
269         }
270
271         return naNum(dv);
272     }
273
274     case props::STRING:
275     case props::UNSPECIFIED:
276         return NASTR(node->getStringValue());
277     case props::VEC3D:
278         return makeVectorFromVec(c, node->getValue<SGVec3d>());
279     case props::VEC4D:
280         return makeVectorFromVec(c, node->getValue<SGVec4d>());
281     default:
282         return naNil();
283     }
284 }
285
286 template<typename T>
287 T makeVecFromVector(naRef vector)
288 {
289     T vec;
290     const int num_components
291         = sizeof(vec.data()) / sizeof(typename T::value_type);
292     int size = naVec_size(vector);
293
294     for (int i = 0; i < num_components && i < size; ++i) {
295         naRef element = naVec_get(vector, i);
296         naRef n = naNumValue(element);
297         if (!naIsNil(n))
298             vec[i] = n.num;
299     }
300     return vec;
301 }
302
303
304 // Set the value of a node; returns true if it succeeded or
305 // false if it failed. <val> can be a string, number, or a
306 // vector or numbers (for SGVec3D/4D types).
307 // Forms:
308 //    props.Node.setValue(string relative_path,
309 //                        val);
310 //    props.Node.setValue(val);
311 static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
312 {
313     NODEARG();
314     MOVETARGET(naVec_size(argv) > 1, true);
315     naRef val = naVec_get(argv, 0);
316     bool result = false;
317     if(naIsString(val)) {
318          result = node->setStringValue(naStr_data(val));
319     } else if(naIsVector(val)) {
320         if(naVec_size(val) == 3)
321             result = node->setValue(makeVecFromVector<SGVec3d>(val));
322         else if(naVec_size(val) == 4)
323             result = node->setValue(makeVecFromVector<SGVec4d>(val));
324         else
325             naRuntimeError(c, "props.setValue() vector value has wrong size");
326     } else {
327         if(!naIsNum(val))
328             naRuntimeError(c, "props.setValue() with non-number");
329
330         double d = naNumValue(val).num;
331         if (SGMisc<double>::isNaN(d)) {
332           naRuntimeError(c, "props.setValue() passed a NaN");
333         }
334
335         result = node->setDoubleValue(d);
336     }
337     return naNum(result);
338 }
339
340 static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
341 {
342     NODEARG();
343     MOVETARGET(naVec_size(argv) > 1, true);
344     // Original code:
345     //   int iv = (int)naNumValue(naVec_get(argv, 0)).num;
346
347     // Junk to pacify the gcc-2.95.3 optimizer:
348     naRef tmp0 = naVec_get(argv, 0);
349     naRef tmp1 = naNumValue(tmp0);
350     if(naIsNil(tmp1))
351         naRuntimeError(c, "props.setIntValue() with non-number");
352     double tmp2 = tmp1.num;
353     int iv = (int)tmp2;
354
355     return naNum(node->setIntValue(iv));
356 }
357
358 static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef* args)
359 {
360     NODEARG();
361     MOVETARGET(naVec_size(argv) > 1, true);
362     naRef val = naVec_get(argv, 0);
363     return naNum(node->setBoolValue(naTrue(val) ? true : false));
364 }
365
366 static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef* args)
367 {
368     NODEARG();
369     MOVETARGET(naVec_size(argv) > 1, true);
370     naRef r = naNumValue(naVec_get(argv, 0));
371     if (naIsNil(r))
372         naRuntimeError(c, "props.setDoubleValue() with non-number");
373
374     if (SGMisc<double>::isNaN(r.num)) {
375       naRuntimeError(c, "props.setDoubleValue() passed a NaN");
376     }
377
378     return naNum(node->setDoubleValue(r.num));
379 }
380
381
382 // Get the parent of this node as a ghost.
383 // Forms:
384 //    props.Node.getParent();
385 static naRef f_getParent(naContext c, naRef me, int argc, naRef* args)
386 {
387     NODENOARG();
388     SGPropertyNode* n = node->getParent();
389     if(!n) return naNil();
390     return propNodeGhostCreate(c, n);
391 }
392
393
394 // Get a child by name and optional index=0, creating if specified (by default it
395 // does not create it). If the node does not exist and create is false, then it
396 // returns nil, else it returns a (possibly new) property ghost.
397 // Forms:
398 //    props.Node.getChild(string relative_path,
399 //                        int index=0,
400 //                        bool create=false);
401 static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
402 {
403     NODEARG();
404     naRef child = naVec_get(argv, 0);
405     if(!naIsString(child)) return naNil();
406     naRef idx = naNumValue(naVec_get(argv, 1));
407     bool create = naTrue(naVec_get(argv, 2)) != 0;
408     SGPropertyNode* n;
409     try {
410         if(naIsNil(idx)) {
411             n = node->getChild(naStr_data(child), create);
412         } else {
413             n = node->getChild(naStr_data(child), (int)idx.num, create);
414         }
415     } catch (const string& err) {
416         naRuntimeError(c, (char *)err.c_str());
417         return naNil();
418     }
419     if(!n) return naNil();
420     return propNodeGhostCreate(c, n);
421 }
422
423
424 // Get all children with a specified name as a vector of ghosts.
425 // Forms:
426 //    props.Node.getChildren(string relative_path);
427 //    props.Node.getChildren(); #get all children
428 static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
429 {
430     NODEARG();
431     naRef result = naNewVector(c);
432     if(naIsNil(argv) || naVec_size(argv) == 0) {
433         // Get all children
434         for(int i=0; i<node->nChildren(); i++)
435             naVec_append(result, propNodeGhostCreate(c, node->getChild(i)));
436     } else {
437         // Get all children of a specified name
438         naRef name = naVec_get(argv, 0);
439         if(!naIsString(name)) return naNil();
440         try {
441             vector<SGPropertyNode_ptr> children
442                 = node->getChildren(naStr_data(name));
443             for(unsigned int i=0; i<children.size(); i++)
444                 naVec_append(result, propNodeGhostCreate(c, children[i]));
445         } catch (const string& err) {
446             naRuntimeError(c, (char *)err.c_str());
447             return naNil();
448         }
449     }
450     return result;
451 }
452
453
454 // Append a named child at the first unused index...
455 // Forms:
456 //    props.Node.addChild(string name,
457 //                        int min_index=0,
458 //                        bool append=true);
459 static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
460 {
461     NODEARG();
462     naRef child = naVec_get(argv, 0);
463     if(!naIsString(child)) return naNil();
464     naRef ref_min_index = naNumValue(naVec_get(argv, 1));
465     naRef ref_append = naVec_get(argv, 2);
466     SGPropertyNode* n;
467     try
468     {
469       int min_index = 0;
470       if(!naIsNil(ref_min_index))
471         min_index = ref_min_index.num;
472
473       bool append = true;
474       if(!naIsNil(ref_append))
475         append = naTrue(ref_append) != 0;
476
477       n = node->addChild(naStr_data(child), min_index, append);
478     }
479     catch (const string& err)
480     {
481       naRuntimeError(c, (char *)err.c_str());
482       return naNil();
483     }
484
485     return propNodeGhostCreate(c, n);
486 }
487
488 static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
489 {
490     NODEARG();
491     naRef child = naVec_get(argv, 0);
492     if(!naIsString(child)) return naNil();
493     naRef ref_count = naNumValue(naVec_get(argv, 1));
494     naRef ref_min_index = naNumValue(naVec_get(argv, 2));
495     naRef ref_append = naVec_get(argv, 3);
496     try
497     {
498       size_t count = 0;
499       if( !naIsNum(ref_count) )
500         throw string("props.addChildren() missing number of children");
501       count = ref_count.num;
502
503       int min_index = 0;
504       if(!naIsNil(ref_min_index))
505         min_index = ref_min_index.num;
506
507       bool append = true;
508       if(!naIsNil(ref_append))
509         append = naTrue(ref_append) != 0;
510
511       const simgear::PropertyList& nodes =
512         node->addChildren(naStr_data(child), count, min_index, append);
513
514       naRef result = naNewVector(c);
515       for( size_t i = 0; i < nodes.size(); ++i )
516         naVec_append(result, propNodeGhostCreate(c, nodes[i]));
517       return result;
518     }
519     catch (const string& err)
520     {
521       naRuntimeError(c, (char *)err.c_str());
522     }
523
524     return naNil();
525 }
526
527
528 // Remove a child by name and index. Returns it as a ghost.
529 // Forms:
530 //    props.Node.removeChild(string relative_path,
531 //                           int index);
532 static naRef f_removeChild(naContext c, naRef me, int argc, naRef* args)
533 {
534     NODEARG();
535     naRef child = naVec_get(argv, 0);
536     naRef index = naVec_get(argv, 1);
537     if(!naIsString(child) || !naIsNum(index)) return naNil();
538     SGPropertyNode_ptr n = 0;
539     try {
540         n = node->removeChild(naStr_data(child), (int)index.num, false);
541     } catch (const string& err) {
542         naRuntimeError(c, (char *)err.c_str());
543     }
544     return propNodeGhostCreate(c, n);
545 }
546
547
548 // Remove all children with specified name. Returns a vector of all the nodes
549 // removed as ghosts.
550 // Forms:
551 //    props.Node.removeChildren(string relative_path);
552 //    props.Node.removeChildren(); #remove all children
553 static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
554 {
555     NODEARG();
556     naRef result = naNewVector(c);
557     if(naIsNil(argv) || naVec_size(argv) == 0) {
558         // Remove all children
559         for(int i = node->nChildren() - 1; i >=0; i--)
560             naVec_append(result, propNodeGhostCreate(c, node->removeChild(i, false)));
561     } else {
562         // Remove all children of a specified name
563         naRef name = naVec_get(argv, 0);
564         if(!naIsString(name)) return naNil();
565         try {
566             vector<SGPropertyNode_ptr> children
567                 = node->removeChildren(naStr_data(name), false);
568             for(unsigned int i=0; i<children.size(); i++)
569                 naVec_append(result, propNodeGhostCreate(c, children[i]));
570         } catch (const string& err) {
571             naRuntimeError(c, (char *)err.c_str());
572             return naNil();
573         }
574     }
575     return result;
576 }
577
578 // Remove all children of a property node.
579 // Forms:
580 //    props.Node.removeAllChildren();
581 static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef* args)
582 {
583   NODENOARG();
584   node->removeAllChildren();
585   return propNodeGhostCreate(c, node);
586 }
587
588 // Alias this property to another one; returns 1 on success or 0 on failure
589 // (only applicable to tied properties).
590 // Forms:
591 //    props.Node.alias(string global_path);
592 //    props.Node.alias(prop_ghost node);
593 //    props.Node.alias(props.Node node); #added by props.nas
594 static naRef f_alias(naContext c, naRef me, int argc, naRef* args)
595 {
596     NODEARG();
597     SGPropertyNode* al;
598     naRef prop = naVec_get(argv, 0);
599     try {
600         if(naIsString(prop)) al = globals->get_props()->getNode(naStr_data(prop), true);
601         else if(naIsGhost(prop)) al = *(SGPropertyNode_ptr*)naGhost_ptr(prop);
602         else throw string("props.alias() with bad argument");
603     } catch (const string& err) {
604         naRuntimeError(c, (char *)err.c_str());
605         return naNil();
606     }
607     return naNum(node->alias(al));
608 }
609
610
611 // Un-alias this property. Returns 1 on success or 0 on failure (only
612 // applicable to tied properties).
613 // Forms:
614 //    props.Node.unalias();
615 static naRef f_unalias(naContext c, naRef me, int argc, naRef* args)
616 {
617     NODENOARG();
618     return naNum(node->unalias());
619 }
620
621
622 // Get the alias of this node as a ghost.
623 // Forms:
624 //    props.Node.getAliasTarget();
625 static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef* args)
626 {
627     NODENOARG();
628     return propNodeGhostCreate(c, node->getAliasTarget());
629 }
630
631
632 // Get a relative node. Returns nil if it does not exist and create is false,
633 // or a ghost object otherwise (wrapped into a props.Node object by props.nas).
634 // Forms:
635 //    props.Node.getNode(string relative_path,
636 //                       bool create=false);
637 static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
638 {
639     NODEARG();
640     naRef path = naVec_get(argv, 0);
641     bool create = naTrue(naVec_get(argv, 1)) != 0;
642     if(!naIsString(path)) return naNil();
643     SGPropertyNode* n;
644     try {
645         n = node->getNode(naStr_data(path), create);
646     } catch (const string& err) {
647         naRuntimeError(c, (char *)err.c_str());
648         return naNil();
649     }
650     return propNodeGhostCreate(c, n);
651 }
652
653
654 // Create a new property node.
655 // Forms:
656 //    props.Node.new();
657 static naRef f_new(naContext c, naRef me, int argc, naRef* args)
658 {
659     return propNodeGhostCreate(c, new SGPropertyNode());
660 }
661
662
663 // Get the global root node (cached by props.nas so that it does
664 // not require a function call).
665 // Forms:
666 //    props._globals()
667 //    props.globals
668 static naRef f_globals(naContext c, naRef me, int argc, naRef* args)
669 {
670     return propNodeGhostCreate(c, globals->get_props());
671 }
672
673 static struct {
674     naCFunction func;
675     const char* name;
676 } propfuncs[] = {
677     { f_getType,            "_getType"            },
678     { f_getAttribute,       "_getAttribute"       },
679     { f_setAttribute,       "_setAttribute"       },
680     { f_getName,            "_getName"            },
681     { f_getIndex,           "_getIndex"           },
682     { f_equals,             "_equals"             },
683     { f_getValue,           "_getValue"           },
684     { f_setValue,           "_setValue"           },
685     { f_setIntValue,        "_setIntValue"        },
686     { f_setBoolValue,       "_setBoolValue"       },
687     { f_setDoubleValue,     "_setDoubleValue"     },
688     { f_getParent,          "_getParent"          },
689     { f_getChild,           "_getChild"           },
690     { f_getChildren,        "_getChildren"        },
691     { f_addChild,           "_addChild"           },
692     { f_addChildren,        "_addChildren"        },
693     { f_removeChild,        "_removeChild"        },
694     { f_removeChildren,     "_removeChildren"     },
695     { f_removeAllChildren,  "_removeAllChildren"  },
696     { f_alias,              "_alias"              },
697     { f_unalias,            "_unalias"            },
698     { f_getAliasTarget,     "_getAliasTarget"     },
699     { f_getNode,            "_getNode"            },
700     { f_new,                "_new"                },
701     { f_globals,            "_globals"            },
702     { 0, 0 }
703 };
704
705 naRef FGNasalSys::genPropsModule()
706 {
707     naRef namespc = naNewHash(_context);
708     for(int i=0; propfuncs[i].name; i++)
709         hashset(namespc, propfuncs[i].name,
710                 naNewFunc(_context, naNewCCode(_context, propfuncs[i].func)));
711     return namespc;
712 }