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