Nasal library/io: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
(io Core functions from former plausible.org added)
(Update {{func link}} form)
 
(66 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{WIP}}
{{Nasal Navigation|nocat=1}}
{{Nasal Navigation}}
This page contains documentation for the '''<code>io</code> namespace''' in [[Nasal]]. This namespace provides APIs for input/output (IO) operations on files. The <code>io</code> namespace is sourced from {{fgdata file|Nasal/io.nas}} and {{simgear file|simgear/nasal/iolib.c}}.
== Nasal Core functions ==
=== io.open(filename, mode="r") ===
Opens the file with the specified mode (as per ANSI fopen()) and returns a ghost object representing the filehandle. Failures are thrown as runtime errors as per die().
=== io.close(filehandle) ===
Closes the specified file as per ANSI fclose().
=== io.read(filehandle, buf, len) ===
Attempts to read length bytes from the filehandle into the beginning of the mutable string buf. Failures (including overruns when length > size(buf)) are thrown as runtime errors as per die(). Returns the number of bytes successfully read.  
=== io.write(filehandle, str) ===
Attempts to write the entirety of the specified string to the filehandle. Failures are thrown as runtime errors as per die(). Returns the number of bytes successfully written.
=== io.seek(filehandle, position, whence)
As ANSI fseek(). Attempts to seek to the specified position based on the whence value (which must be one of io.SEEK_SET, io.SEEK_END, or io.SEEK_CUR).
=== io.tell(filehandle) ===
Returns the current seek position of the filehandle.
=== io.readln(filehandle) ===
Reads and returns a single text line from the filehandle. Interprets both "\n" and "\r\n" as end of line markers, and does not include the "\r" or "\n" bytes in the returned string. End of file or error is signaled by returning nil.
=== io.stat(filename) ===
Calls unix or win32 stat() on the specified file name and returns a seven element array whose contents are, in order: dev, ino, mode, nlink, uid, gid, rdef, size, atime, mtime, ctime. Errors are signaled as exceptions as per die().  


== Reading files ==
== Functions ==
The following description of functions refers to "$FG_ROOT\data\Nasal\io.nas".  
=== basename() ===
=== io.basename() ===
{{Nasal doc
Works like standard Unix command basename. Returns the file name from a given path.
|syntax = io.basename(path);
|version = 3.0
|commit = {{fgdata commit|6ae3fa|t=commit}}
|text = Returns last element of a path as a string.
|param1 = path
|param1text = Path as a string.
|example1 = var path = '/demo/demo.xml';
print(io.basename(path)); # prints "demo.xml"
|example2 = var path = 'C:\FlightGear\FlightGear 3.2.0\data';
print(io.basename(path)); # prints "data"
}}
 
=== close() ===
{{Nasal doc
|syntax = io.close(file);
|text = Flushes and closes the specified file. Returns <code>'''nil'''</code>.
|param1 = file
|param1text = File object as returned by {{func link|open()|page=this}}.
|example1 = var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var file = io.open(path);
print(io.readln(file)); # prints the XML header
io.close(file);
}}
 
=== dirname() ===
{{Nasal doc
|syntax = io.dirname(path);
|version = 3.0
|commit = {{fgdata commit|6ae3fa|t=commit}}
|text = Returns the directory of a path as a string.
|param1 = path
|param1text = Path as a string.
|example1 = var path = '/demo/demo.xml';
print(io.dirname(path)); # prints "/demo/"
|example2 = var path = 'C:\FlightGear\FlightGear 3.2.0\data';
print(io.dirname(path)); # prints "C:/FlightGear/FlightGear 3.2.0/"
}}
 
=== flush() ===
{{Nasal doc
|syntax = io.flush(file);
|text = Flushes the file's buffer. This means that the contents of the buffer (a kind of temporary storage) are written to the actual file on the hard disk. Note that the buffer is also flushed when {{func link|close()|page=this}} is called.
|param1 = file
|param1text = File object as returned by {{func link|open()|page=this}}.
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "w"); # create and open file
io.write(file, "Hello, World!"); # write (to the buffer)
 
var file2 = io.open(path); # open the file separatly
var b = bits.buf(13); # create a buffer
io.read(file2, b, 13); # read file
print(b); # prints nothing
 
io.flush(file); # flush buffer to file
io.read(file2, b, 13); # read file again
print(b); # prints "Hello, World!"
 
io.close(file); # close
io.close(file2);
}}
 
=== include() ===
{{Nasal doc
|syntax = io.include(file);
|version = 3.0
|commit = {{fgdata commit|6ae3fa|t=commit}}
|text = Loads a given Nasal file into where the function is called from.
|param1 = file
|param1text = Path to file as a string. If it is just a filename, it will be assumed that the file is in the same folder as calling script. Otherwise, it will be searched for in the standard FlightGear directories (see [[Resolving Paths]]).
|example1 = io.include("Nasal/geo.nas"); # include the geo namespace
var coord = Coord.new(); # create a new Coord object
coord.set_latlon(0, 0, 2000); # set coordinates
print(coord.alt()); # prints "2000"
}}
 
=== load_nasal() ===
{{Nasal doc
|syntax = io.load_nasal(file[, module]);
|text = Loads and executes a given Nasal script into a namespace.
|param1 = file
|param1text = Full path to the Nasal file as a string.
|param2 = module
|param2text = Optional name of module to load the script into as a string. If not given, the namespace will be the name of the file.
|example1text = For the examples, first put the below code in to a new file, <tt>''[[$FG_HOME]]/Export/demo.nas''</tt>:
<syntaxhighlight lang="nasal">
<syntaxhighlight lang="nasal">
io.basename(<path>);
var sayHello = func(){
</syntaxhighlight>
    print("Hello, World!");
=== io.dirname() ===
}
Works like standard Unix command dirname. Returns the directory part from a given path.
<syntaxhighlight lang="nasal">
io.basename(<path>);
</syntaxhighlight>
=== io.readfile() ===
Reads and returns a complete file as a string.
<syntaxhighlight lang="nasal">
io.readfile(file);
</syntaxhighlight>
</syntaxhighlight>
|example1 = var file = getprop("/sim/fg-home") ~ '/Export/demo.nas';
io.load_nasal(file); # load into "demo" namespace
demo.sayHello(); # "Hello, World!" will be printed
|example2 = var file = getprop("/sim/fg-home") ~ '/Export/demo.nas';
io.load_nasal(file, "myDemo"); # load into "myDemo" namespace
myDemo.sayHello(); # "Hello, World!" will be printed
}}
=== open() ===
{{Nasal doc
|syntax = io.open(path[, mode]);
|text = Opens the file with the specified mode and returns an <code>iofile</code> ghost object representing the file.
{{{!}} class="wikitable"
! Mode !! Meaning
{{!-}}
{{!}} <code>r</code>
{{!}} Opens a file for input (reading) operations. The file must exist.
{{!-}}
{{!}} <code>w</code>
{{!}} Creates a new file for output (writing) operations. If the file already exists, its contents will be cleared first.
{{!-}}
{{!}} <code>a</code>
{{!}} Opens a file for appending data. The file is created if it does not exist.
{{!-}}
{{!}} <code>r+</code>
{{!}} Open for input and output operations. The file must exist.
{{!-}}
{{!}} <code>w+</code>
{{!}} Creates a new file for input and output operations. If the file already exists, its contents will be cleared first.
{{!-}}
{{!}} <code>a+</code>
{{!}} Opens a file for input and output operations. Output operations will append data to the end of the file. Input operations are affected by {{func link|seek()|page=this}}, but output operations will move the position back to the end of the file. The file is created if it does not exist.
{{!-}}
{{!}} <code>rb</code>
{{!}} Opens a file for input operations, treating it as a binary file. The file must exist. '''Default mode'''.
{{!-}}
{{!}} <code>wb</code>
{{!}} Creates a new file for output operations, treating it as a binary file. If the file already exists, its contents will be cleared first.
{{!-}}
{{!}} <code>ab</code>
{{!}} Opens a file for appending data, treating it as a binary file. The file is created if it does not exist.
{{!-}}
{{!}} <code>r+b</code> ''or'' <code>rb+</code>
{{!}} Open for input and output operations, treating it as a binary file. The file must exist.
{{!-}}
{{!}} <code>w+b</code> ''or'' <code>wb+</code>
{{!}} Creates a new file for input and output operations, treating it as a binary file. If the file already exists, its contents will be cleared first.
{{!-}}
{{!}} <code>a+b</code> ''or'' <code>ab+</code>
{{!}} Opens a file for input and output operations, treating it as a binary file. Output operations will append data to the end of the file. Input operations are affected by {{func link|seek()|page=this}}, but output operations will move the position back to the end of the file. The file is created if it does not exist.
{{!}}}
|param1 = path
|param1text = Full path to the file as a string.
|param2 = mode
|param2text = Optional mode parameter. See the table below. Defaults to "rb".
|example1text = Note that the below example must be run first for the others to work.
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "w"); # open in write mode
var text = 'Hello, World!' ~ "\r";
io.write(file, text);
io.close(file);
|example2 = var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "r"); # open in read mode
print(io.readln(file)); # prints "Hello, World!"
io.close(file);
|example3 = var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "a"); # open in append mode
var text = "\n" ~ 'This is line 2';
io.write(file, text);
io.close(file);
}}
=== read() ===
{{Nasal doc
|syntax = io.read(file, buf, len);
|text = Reads a given number of bytes from the given file and places those bytes into the given buffer. The bytes will be read from the position of the position indicator (this can be changed using {{func link|seek()|page=this}}). Failures are thrown as runtime errors. Returns the number of bytes successfully read.
|param1 = file
|param1text = File object as returned by {{func link|open()|page=this}}.
|param2 = buf
|param2text = Buffer to read bytes into. A new buffer can be created using <code>bits.buf(size)</code>. Must not be smaller than '''len'''.
|param3 = len
|param3text = Number of bytes to read as an integer. The position indicator will be advanced by this number of bytes. Must not be greater than the size of '''buf'''.
|example1 = var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var file = io.open(path);
var len = 5;
var buf = bits.buf(len);
io.read(file, buf, len);
print(buf); # prints "# geo"
io.read(file, buf, len);
print(buf); # prints " func"
io.close(file);
}}
=== read_airport_properties() ===
{{Nasal doc
|syntax = io.read_airport_properties(icao, fname[, target]);
|version = 2.4
|commit = {{fgdata commit|d4fb11|t=commit}}
|text = Loads an airport-related XML file. Paths will be concatenated in the following form: <tt>''[[$FG_SCENERY]]/Airports/'''''i'''''/'''''c'''''/'''''a'''''/'''''icao'''''.'''''fname'''''.xml''</tt>. Returns the data as a <code>props.Node</code> object or <code>'''nil'''</code> on error. Note that the file ''must'' be in the [[PropertyList XML files|PropertyList]] format, or else the function will fail.
|param1 = icao
|param1text = ICAO code of the airport as a string.
|param2 = fname
|param2text = Filename of the airport data file as a string, e.g., "groundnet", "ils", "jetways", "rwyuse", "threshold", or "twr". See above for its use in the concatenation of the path.
|param3 = target
|param3text = Optional place to put the results in the [[Property Tree]]. May be either a <code>props.Node</code> object pointing to a place in the Property Tree or a property path.
|example1 = var data = io.read_airport_properties("KSFO", "ils"); # the airport might need changing
props.dump(data); # dump data
|example2 = var node = props.globals.getNode("demo", 1);
io.read_airport_properties("KSFO", "ils", node); # the airport might need changing
props.dump(node); # dump data
|example3 = var path = "/demo";
io.read_airport_properties("KSFO", "ils", path); # the airport might need changing
props.dump(props.globals.getNode(path)); # dump data
}}
=== read_properties() ===
{{Nasal doc
|syntax = io.read_properties(path[, target]);
|text = Loads an XML file. Returns the data as a <code>props.Node</code> object on success or <code>'''nil'''</code> on error. Note that the file ''must'' be in the [[PropertyList XML files|PropertyList]] format, or else the function will fail.
|param1 = path
|param1text = Path to the XML file as a string.
|param2 = target
|param2text = Optional place to put the results in the [[Property Tree]]. May be either a <code>props.Node</code> object pointing to a place in the Property Tree or a property path.
|example1 = var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var data = io.read_properties(path);
props.dump(data); # dump data
|example2 = var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var node = props.globals.getNode("demo", 1);
io.read_properties(path, node);
props.dump(node); # dump data
|example3 = var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var node = "/demo";
io.read_properties(path, node);
props.dump(props.globals.getNode(node)); # dump data
}}


== Read FG PropertyList XML ==
=== readfile() ===
The following description of functions refers to "$FG_ROOT\data\Nasal\io.nas".
{{Nasal doc
=== io.read_properties() ===
|syntax = io.readfile(file);
Load XML file in FlightGear's native <PropertyList> format.        
|text = Reads and returns a complete file as a string. Failures are thrown as runtime errors.
If the second, optional target parameter is set, then the properties
|param1 = file
are loaded to this node in the global property tree. Otherwise they 
|param1text = Path to the file as a string.
are returned as a separate props.Node tree. Returns the data as a   
|example1 = var file = getprop("/sim/fg-root") ~ '/Nasal/math.nas';
props.Node on success or nil on error.
file = io.readfile(file);
<syntaxhighlight lang="nasal">
print(file);
Usage:  io.read_properties(<filename> [, <props.Node or property-path>]);
}}
</syntaxhighlight>


Examples:
=== readln() ===
<syntaxhighlight lang="nasal">
{{Nasal doc
var target = props.globals.getNode("/sim/model");          
|syntax = io.readln(file);
io.read_properties("/tmp/foo.xml", target);                
|text = Reads and returns a single line from the file, advancing the position indicator. Accepts different {{wikipedia|newline}} types, including {{abbr|LF|line feed}}, {{abbr|CR|carriage return}}, and CR+LF. Does not include the EOL character(s) in the returned string. End of file or an error is signaled by returning <code>'''nil'''</code>.
                                                           
|param1 = file
var data = io.read_properties("/tmp/foo.xml", "/sim/model");
|param1text = File object as returned by {{func link|open()|page=this}}.
var data = io.read_properties("/tmp/foo.xml");            
|example1 = var path = getprop("/sim/fg-root") ~ '/Nasal/bits.nas';
</syntaxhighlight>
var file = io.open(path);
var line = io.readln(file);
print(line); # prints "var bit = [var _ = 1];"
line = io.readln(file);
print(line); # prints "for (var i = 1; i < 32; i += 1)"
}}


=== io.read_airport_properties() ===
=== readxml() ===
Load XML file in FlightGear's native <PropertyList> format.        
{{Nasal doc
File will be located in the airport-scenery directories according to  
|syntax = io.readxml(path[, prefix]);
ICAO and filename, i,e in Airports/I/C/A/ICAO.filename.xml         
|text = Reads an XML file from an absolute path and returns it as a <code>props.Node</code> object. All nodes will be of the string type. Also, all attributes will be converted in subnodes with a prefix appended. Returns <code>'''nil'''</code> on error.
|param1 = path
|param1text = Absolute path to the XML file.
|param2 = prefix
|param2text = Optional prefix to give to the attribute nodes. If it is <code>'''nil'''</code>, then attributes will be ignored. Defaults to "___" (three underscores).
|example1text = First, put the following code into <tt>''[[$FG_HOME]]/Export/demo.xml''</tt>:
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="UTF-8"?>


If the second, optional target parameter is set, then the properties are loaded to this node in the global property tree.
<foo>
Otherwise they are returned as a separate props.Node tree.
  <bar attr1="Hello" attr2="World">abc</bar>
Returns the data as a props.Node on success or nil on error.
   <bar>xyz</bar>
<syntaxhighlight lang="nasal">
</foo>
Usage:   io.read_properties(<filename> [, <props.Node or property-path>]);
</syntaxhighlight>
</syntaxhighlight>
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var data = io.readxml(path);
props.dump(data); # dump data
}}


Example:
=== seek() ===
<syntaxhighlight lang="nasal">
{{Nasal doc
var data = io.read_airport_properties("KSFO", "rwyuse");
|syntax = io.seek(file, position, whence);
</syntaxhighlight>
|text = Moves the pointer position to a specified place.
|param1 = file
|param1text = File object as returned by {{func link|open()|page=this}}.
|param2 = position
|param2text = Number of bytes offset from '''whence''' as an integer. If '''whence''' is {{func link|SEEK_SET|page=this}}, this cannot be negative.
|param3 = whence
|param3text = Specifies position to set the offset from. Must be one of {{func link|SEEK_SET|page=this}} (beginning of file), {{func link|SEEK_END|page=this}} (end of file), or {{func link|SEEK_CUR|page=this}} (current pointer position).
|example1 = var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var file = io.open(path, "r"); # open file
var len = 15;
var buf = bits.buf(len);
io.seek(file, 122, io.SEEK_SET); # set to the desired position
io.read(file, buf, len); # read file
print(buf); # prints "geo.Coord class"
io.close(file); # close file
}}
 
=== stat() ===
{{Nasal doc
|syntax = io.stat(path);
|text = Calls {{wikipedia|stat (system call)|stat}} and returns a vector containing 12 pieces of data. See the table below for a list of them in order from first in the vector to last. Returns <code>'''nil'''</code> if the file does not exist.
{{{!}} class="wikitable"
! Data !! Meaning
{{!-}}
{{!}} dev {{!!}} Identifier of device containing file
{{!-}}
{{!}} ino {{!!}} Inode number
{{!-}}
{{!}} mode {{!!}} Protection mode
{{!-}}
{{!}} nlink {{!!}} Reference count of hard links
{{!-}}
{{!}} uid {{!!}} User identifier of owner
{{!-}}
{{!}} gid {{!!}} Group identifier of owner
{{!-}}
{{!}} rdev {{!!}} Device identifier (if special file)
{{!-}}
{{!}} size {{!!}} Total file size, in bytes
{{!-}}
{{!}} atime {{!!}} Time of last access as a Unix timestamp
{{!-}}
{{!}} mtime {{!!}} Time of last modification as a Unix timestamp
{{!-}}
{{!}} ctime {{!!}} Time of last status change as a Unix timestamp
{{!-}}
{{!}} type {{!!}} Type of file. May be one of "unk", "reg", "dir", "chr", "blk", "fifo", "lnk", or "sock".<br>See {{simgear file|simgear/nasal/iolib.c|l=214}}.
{{!}}}
|param1 = path
|param1text = Path to the file as a string.
|example1 = var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var stat = io.stat(path);
printf("File size: %s bytes", stat[7]); # prints file size
}}


== Write FG PropertyList XML ==
=== tell() ===
The following description of functions refers to "$FG_ROOT\data\Nasal\io.nas".
{{Nasal doc
=== io.write_properties() ===
|syntax = io.tell(file);
Write XML file in FlightGear's native <PropertyList> format.   
|text = Returns the current pointer position of the file.
Returns the filename on success or nil on error. If the source is a props.Node that refers to a node in the main tree,
|param1 = file
then the data are directly written from the tree, yielding a more accurate result.
|param1text = File object as returned by {{func link|open()|page=this}}.
Otherwise the data need to be copied first, which may slightly change node types (FLOAT becomes DOUBLE etc.)
|example1 = var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
<syntaxhighlight lang="nasal">
var file = io.open(path);
Usage:  io.write_properties(<filename>, <props.Node or property-path>);
io.seek(file, 2, io.SEEK_SET);
</syntaxhighlight>
print(io.tell(file)); # prints "2"
Examples:
io.close(file);
<syntaxhighlight lang="nasal">
}}
var data = props.Node.new({ a:1, b:2, c:{ d:3, e:4 } });  
io.write_properties("/tmp/foo.xml", data);             
io.write_properties("/tmp/foo.xml", "/sim/model");      
</syntaxhighlight>


== Read/Write Plain XML ==
=== write() ===
This description of functions refers to "$FG_ROOT\data\Nasal\io.nas".
{{Nasal doc
|syntax = io.write(file, string);
|text = Writes a string to a given file and returns the number of bytes successfully written.
|param1 = file
|param1text = File object as returned by {{func link|open()|page=this}}.
|param2 = string
|param2text = String to write to the file.
{{tip|It is good practice to make sure that all lines, including the last one, end in a newline (<code>\n</code>). This will especially help if you will be calling {{func link|readln()|page=this}} on the file later.}}
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "wb"); # open in write mode
var str = 'Hello World!' ~ "\n";
io.write(file, str); # write the data
io.close(file); # close (and flush) the file stream
}}


The following two functions are for reading generic XML files into   
=== write_properties() ===
the property tree and for writing them from there to the disk. The   
{{Nasal doc
built-in fgcommands (load, save, loadxml, savexml) are for FlightGear's
|syntax = io.write_properties(path, prop);
own <PropertyList> XML files only, as they only handle a limited     
|text = Writes nodes from the [[Property Tree]] to an XML file in FlightGear's [[PropertyList XML files|PropertyList]] format. If the file does not exist, it will be created. If it does, all contents will be overwritten. Returns the filename on success or <code>'''nil'''</code> on error.
number of very specific attributes. The io.readxml() loader turns     
|param1 = path
attributes into regular children with a configurable prefix prepended 
|param1text = Path of the file to write to as a string. If it does not have a <tt>.xml</tt> extension, <tt>.xml</tt> will be added to it.
to their name, while io.writexml() turns such nodes back into         
|param2 = prop
attributes. The two functions have their own limitations, but can     
|param2text = Either a property path or a <code>props.Node</code> object. Note that, when the latter is used, results will be more accurate if it refers to a node in the Property Tree, otherwise the node will have to be copied, which may change the node type.
easily get extended to whichever needs. The underlying parsexml()    
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
command will handle any XML file.
var prop = "/position";
print(io.write_properties(path, prop));
|example2 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var prop = props.globals.getNode("position");
print(io.write_properties(path, prop));
|example3 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "a": 12.34,
    "b": "Hello, World!",
    "c": {
        "d": [1, 2, 3]
    }
};
var prop = props.Node.new(tree);
print(io.write_properties(path, prop));
}}


=== io.readxml() ===
=== writexml() ===
Reads an XML file from an absolute path and returns it as property 
{{Nasal doc
tree. All nodes will be of type STRING. Data are only written to  
|syntax = io.basename(path, node[, indent[, prefix]]);
leafs. Attributes are written as regular nodes with the optional   
|text = Writes a node structure to an XML file.
prefix prepended to the name. If the prefix is nil, then attributes  
|param1 = path
are ignored. Returns nil on error.
|param1text = Path of the file to write to as a string. If it does not have a <tt>.xml</tt> extension, <tt>.xml</tt> will be added to it.
<syntaxhighlight lang="nasal">
|param2 = node
Usage: io.readxml(path[,prefix = "___"]);
|param2text = <code>props.Node</code> object containing the data to write to the file. Attributes should be included as child nodes with a certain '''prefix'''. See also {{func link|readxml()|page=this}}. It must also have just one root node.
</syntaxhighlight>
|param3 = indent
<syntaxhighlight lang="nasal">
|param3text = Optional string specifying what characters should be used to indent lines. Defaults to one horizontal tab character (<tt>\t</tt>).
io.readxml(path,prefix);
|param4 = prefix
</syntaxhighlight>
|param4text = Optional string specifying what prefixes attributes will have.
|example1 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "source": {
        ___type: "book", # will become an attribute
        "title": "The Grand Book of Code",
        "author-last": "Echter",
        "author-first": "William C.",
        "year": "2011",
        "publisher": "Pen Publishers",
        "city": "Manchester"
    }
};
var node = props.Node.new(tree);
io.writexml(path, node);
|example2 = var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "source": {
        _attr_type: "book",
        "title": "The Grand Book of Code",
        "author-last": "Echter",
        "author-first": "William C.",
        "year": "2011",
        "publisher": "Pen Publishers",
        "city": "Manchester"
    }
};
var node = props.Node.new(tree);
io.writexml(path, node, "  ", "_attr_"); # indent using two spaces
}}


=== io.writexml() ===
== Variables ==
Writes a property tree as returned by readxml() to a file. Children 
=== SEEK_CUR ===
with name starting with <prefix> are again turned into attributes of 
{{Nasal doc
their parent. <node> must contain exactly one child, which will      
|syntax = io.SEEK_CUR;
become the XML file's outermost element.                            
|text = Used with {{func link|seek()|page=this}}. Means the offset will be counted from the current pointer postion.
<syntaxhighlight lang="nasal">
}}
Usage:  io.writexml(path, node[,indent = "\t"][, prefix = "___"]);
=== SEEK_END ===
</syntaxhighlight>
{{Nasal doc
<syntaxhighlight lang="nasal">
|syntax = io.SEEK_END;
io.writexml(path, node, indent,prefix);
|text = Used with {{func link|seek()|page=this}}. Means the offset will be counted from the end of the file.
</syntaxhighlight>
}}
=== SEEK_SET ===
{{Nasal doc
|syntax = io.SEEK_SET;
|text = Used with {{func link|seek()|page=this}}. Means the offset will be counted from the beginning of the file.
}}


== Serializing Nasal types ==
{{Nasal namespaces}}

Latest revision as of 18:46, 16 November 2016

This page contains documentation for the io namespace in Nasal. This namespace provides APIs for input/output (IO) operations on files. The io namespace is sourced from fgdata/Nasal/io.nas and simgear/simgear/nasal/iolib.c.

Functions

basename()

io.basename(path);

Version added: FG 3.0 (commit)

Returns last element of a path as a string.

path
Path as a string.

Examples

var path = '/demo/demo.xml';
print(io.basename(path)); # prints "demo.xml"
var path = 'C:\FlightGear\FlightGear 3.2.0\data';
print(io.basename(path)); # prints "data"

close()

io.close(file);

Flushes and closes the specified file. Returns nil.

file
File object as returned by open() .

Example

var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var file = io.open(path);
print(io.readln(file)); # prints the XML header
io.close(file);

dirname()

io.dirname(path);

Version added: FG 3.0 (commit)

Returns the directory of a path as a string.

path
Path as a string.

Examples

var path = '/demo/demo.xml';
print(io.dirname(path)); # prints "/demo/"
var path = 'C:\FlightGear\FlightGear 3.2.0\data';
print(io.dirname(path)); # prints "C:/FlightGear/FlightGear 3.2.0/"

flush()

io.flush(file);

Flushes the file's buffer. This means that the contents of the buffer (a kind of temporary storage) are written to the actual file on the hard disk. Note that the buffer is also flushed when close() is called.

file
File object as returned by open() .

Example

var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "w"); # create and open file
io.write(file, "Hello, World!"); # write (to the buffer)

var file2 = io.open(path); # open the file separatly
var b = bits.buf(13); # create a buffer
io.read(file2, b, 13); # read file
print(b); # prints nothing

io.flush(file); # flush buffer to file
io.read(file2, b, 13); # read file again
print(b); # prints "Hello, World!"

io.close(file); # close
io.close(file2);

include()

io.include(file);

Version added: FG 3.0 (commit)

Loads a given Nasal file into where the function is called from.

file
Path to file as a string. If it is just a filename, it will be assumed that the file is in the same folder as calling script. Otherwise, it will be searched for in the standard FlightGear directories (see Resolving Paths).

Example

io.include("Nasal/geo.nas"); # include the geo namespace
var coord = Coord.new(); # create a new Coord object
coord.set_latlon(0, 0, 2000); # set coordinates
print(coord.alt()); # prints "2000"

load_nasal()

io.load_nasal(file[, module]);

Loads and executes a given Nasal script into a namespace.

file
Full path to the Nasal file as a string.
module
Optional name of module to load the script into as a string. If not given, the namespace will be the name of the file.

Examples
For the examples, first put the below code in to a new file, $FG_HOME/Export/demo.nas:

var sayHello = func(){
    print("Hello, World!");
}
var file = getprop("/sim/fg-home") ~ '/Export/demo.nas';
io.load_nasal(file); # load into "demo" namespace
demo.sayHello(); # "Hello, World!" will be printed
var file = getprop("/sim/fg-home") ~ '/Export/demo.nas';
io.load_nasal(file, "myDemo"); # load into "myDemo" namespace
myDemo.sayHello(); # "Hello, World!" will be printed

open()

io.open(path[, mode]);

Opens the file with the specified mode and returns an iofile ghost object representing the file.

Mode Meaning
r Opens a file for input (reading) operations. The file must exist.
w Creates a new file for output (writing) operations. If the file already exists, its contents will be cleared first.
a Opens a file for appending data. The file is created if it does not exist.
r+ Open for input and output operations. The file must exist.
w+ Creates a new file for input and output operations. If the file already exists, its contents will be cleared first.
a+ Opens a file for input and output operations. Output operations will append data to the end of the file. Input operations are affected by seek() , but output operations will move the position back to the end of the file. The file is created if it does not exist.
rb Opens a file for input operations, treating it as a binary file. The file must exist. Default mode.
wb Creates a new file for output operations, treating it as a binary file. If the file already exists, its contents will be cleared first.
ab Opens a file for appending data, treating it as a binary file. The file is created if it does not exist.
r+b or rb+ Open for input and output operations, treating it as a binary file. The file must exist.
w+b or wb+ Creates a new file for input and output operations, treating it as a binary file. If the file already exists, its contents will be cleared first.
a+b or ab+ Opens a file for input and output operations, treating it as a binary file. Output operations will append data to the end of the file. Input operations are affected by seek() , but output operations will move the position back to the end of the file. The file is created if it does not exist.
path
Full path to the file as a string.
mode
Optional mode parameter. See the table below. Defaults to "rb".

Examples

Note that the below example must be run first for the others to work.

var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "w"); # open in write mode
var text = 'Hello, World!' ~ "\r";
io.write(file, text);
io.close(file);
var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "r"); # open in read mode
print(io.readln(file)); # prints "Hello, World!"
io.close(file);
var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "a"); # open in append mode
var text = "\n" ~ 'This is line 2';
io.write(file, text);
io.close(file);

read()

io.read(file, buf, len);

Reads a given number of bytes from the given file and places those bytes into the given buffer. The bytes will be read from the position of the position indicator (this can be changed using seek() ). Failures are thrown as runtime errors. Returns the number of bytes successfully read.

file
File object as returned by open() .
buf
Buffer to read bytes into. A new buffer can be created using bits.buf(size). Must not be smaller than len.
len
Number of bytes to read as an integer. The position indicator will be advanced by this number of bytes. Must not be greater than the size of buf.

Example

var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var file = io.open(path);
var len = 5;
var buf = bits.buf(len);
io.read(file, buf, len);
print(buf); # prints "# geo"
io.read(file, buf, len);
print(buf); # prints " func"
io.close(file);

read_airport_properties()

io.read_airport_properties(icao, fname[, target]);

Version added: FG 2.4 (commit)

Loads an airport-related XML file. Paths will be concatenated in the following form: $FG_SCENERY/Airports/i/c/a/icao.fname.xml. Returns the data as a props.Node object or nil on error. Note that the file must be in the PropertyList format, or else the function will fail.

icao
ICAO code of the airport as a string.
fname
Filename of the airport data file as a string, e.g., "groundnet", "ils", "jetways", "rwyuse", "threshold", or "twr". See above for its use in the concatenation of the path.
target
Optional place to put the results in the Property Tree. May be either a props.Node object pointing to a place in the Property Tree or a property path.

Examples

var data = io.read_airport_properties("KSFO", "ils"); # the airport might need changing
props.dump(data); # dump data
var node = props.globals.getNode("demo", 1);
io.read_airport_properties("KSFO", "ils", node); # the airport might need changing
props.dump(node); # dump data
var path = "/demo";
io.read_airport_properties("KSFO", "ils", path); # the airport might need changing
props.dump(props.globals.getNode(path)); # dump data

read_properties()

io.read_properties(path[, target]);

Loads an XML file. Returns the data as a props.Node object on success or nil on error. Note that the file must be in the PropertyList format, or else the function will fail.

path
Path to the XML file as a string.
target
Optional place to put the results in the Property Tree. May be either a props.Node object pointing to a place in the Property Tree or a property path.

Examples

var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var data = io.read_properties(path);
props.dump(data); # dump data
var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var node = props.globals.getNode("demo", 1);
io.read_properties(path, node);
props.dump(node); # dump data
var path = getprop("/sim/fg-root") ~ '/keyboard.xml';
var node = "/demo";
io.read_properties(path, node);
props.dump(props.globals.getNode(node)); # dump data

readfile()

io.readfile(file);

Reads and returns a complete file as a string. Failures are thrown as runtime errors.

file
Path to the file as a string.

Example

var file = getprop("/sim/fg-root") ~ '/Nasal/math.nas';
file = io.readfile(file);
print(file);

readln()

io.readln(file);

Reads and returns a single line from the file, advancing the position indicator. Accepts different newline This is a link to a Wikipedia article types, including LF, CR, and CR+LF. Does not include the EOL character(s) in the returned string. End of file or an error is signaled by returning nil.

file
File object as returned by open() .

Example

var path = getprop("/sim/fg-root") ~ '/Nasal/bits.nas';
var file = io.open(path);
var line = io.readln(file);
print(line); # prints "var bit = [var _ = 1];"
line = io.readln(file);
print(line); # prints "for (var i = 1; i < 32; i += 1)"

readxml()

io.readxml(path[, prefix]);

Reads an XML file from an absolute path and returns it as a props.Node object. All nodes will be of the string type. Also, all attributes will be converted in subnodes with a prefix appended. Returns nil on error.

path
Absolute path to the XML file.
prefix
Optional prefix to give to the attribute nodes. If it is nil, then attributes will be ignored. Defaults to "___" (three underscores).

Example
First, put the following code into $FG_HOME/Export/demo.xml:

<?xml version="1.0" encoding="UTF-8"?>

<foo>
  <bar attr1="Hello" attr2="World">abc</bar>
  <bar>xyz</bar>
</foo>
var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var data = io.readxml(path);
props.dump(data); # dump data

seek()

io.seek(file, position, whence);

Moves the pointer position to a specified place.

file
File object as returned by open() .
position
Number of bytes offset from whence as an integer. If whence is SEEK_SET , this cannot be negative.
whence
Specifies position to set the offset from. Must be one of SEEK_SET (beginning of file), SEEK_END (end of file), or SEEK_CUR (current pointer position).

Example

var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var file = io.open(path, "r"); # open file
var len = 15;
var buf = bits.buf(len);
io.seek(file, 122, io.SEEK_SET); # set to the desired position
io.read(file, buf, len); # read file
print(buf); # prints "geo.Coord class"
io.close(file); # close file

stat()

io.stat(path);

Calls stat This is a link to a Wikipedia article and returns a vector containing 12 pieces of data. See the table below for a list of them in order from first in the vector to last. Returns nil if the file does not exist.

Data Meaning
dev Identifier of device containing file
ino Inode number
mode Protection mode
nlink Reference count of hard links
uid User identifier of owner
gid Group identifier of owner
rdev Device identifier (if special file)
size Total file size, in bytes
atime Time of last access as a Unix timestamp
mtime Time of last modification as a Unix timestamp
ctime Time of last status change as a Unix timestamp
type Type of file. May be one of "unk", "reg", "dir", "chr", "blk", "fifo", "lnk", or "sock".
See simgear/simgear/nasal/iolib.c (line 214).
path
Path to the file as a string.

Example

var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var stat = io.stat(path);
printf("File size: %s bytes", stat[7]); # prints file size

tell()

io.tell(file);

Returns the current pointer position of the file.

file
File object as returned by open() .

Example

var path = getprop("/sim/fg-root") ~ '/Nasal/geo.nas';
var file = io.open(path);
io.seek(file, 2, io.SEEK_SET);
print(io.tell(file)); # prints "2"
io.close(file);

write()

io.write(file, string);

Writes a string to a given file and returns the number of bytes successfully written.

file
File object as returned by open() .
string
String to write to the file.
Tip  It is good practice to make sure that all lines, including the last one, end in a newline (\n). This will especially help if you will be calling readln() on the file later.

Example

var path = getprop("/sim/fg-home") ~ '/Export/demo.txt';
var file = io.open(path, "wb"); # open in write mode
var str = 'Hello World!' ~ "\n";
io.write(file, str); # write the data
io.close(file); # close (and flush) the file stream

write_properties()

io.write_properties(path, prop);

Writes nodes from the Property Tree to an XML file in FlightGear's PropertyList format. If the file does not exist, it will be created. If it does, all contents will be overwritten. Returns the filename on success or nil on error.

path
Path of the file to write to as a string. If it does not have a .xml extension, .xml will be added to it.
prop
Either a property path or a props.Node object. Note that, when the latter is used, results will be more accurate if it refers to a node in the Property Tree, otherwise the node will have to be copied, which may change the node type.

Examples

var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var prop = "/position";
print(io.write_properties(path, prop));
var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var prop = props.globals.getNode("position");
print(io.write_properties(path, prop));
var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "a": 12.34,
    "b": "Hello, World!",
    "c": {
        "d": [1, 2, 3]
    }
};
var prop = props.Node.new(tree);
print(io.write_properties(path, prop));

writexml()

io.basename(path, node[, indent[, prefix]]);

Writes a node structure to an XML file.

path
Path of the file to write to as a string. If it does not have a .xml extension, .xml will be added to it.
node
props.Node object containing the data to write to the file. Attributes should be included as child nodes with a certain prefix. See also readxml() . It must also have just one root node.
indent
Optional string specifying what characters should be used to indent lines. Defaults to one horizontal tab character (\t).
prefix
Optional string specifying what prefixes attributes will have.

Examples

var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "source": {
        ___type: "book", # will become an attribute
        "title": "The Grand Book of Code",
        "author-last": "Echter",
        "author-first": "William C.",
        "year": "2011",
        "publisher": "Pen Publishers",
        "city": "Manchester"
    }
};
var node = props.Node.new(tree);
io.writexml(path, node);
var path = getprop("/sim/fg-home") ~ '/Export/demo.xml';
var tree = {
    "source": {
        _attr_type: "book",
        "title": "The Grand Book of Code",
        "author-last": "Echter",
        "author-first": "William C.",
        "year": "2011",
        "publisher": "Pen Publishers",
        "city": "Manchester"
    }
};
var node = props.Node.new(tree);
io.writexml(path, node, "  ", "_attr_"); # indent using two spaces

Variables

SEEK_CUR

io.SEEK_CUR;

Used with seek() . Means the offset will be counted from the current pointer postion.

SEEK_END

io.SEEK_END;

Used with seek() . Means the offset will be counted from the end of the file.

SEEK_SET

io.SEEK_SET;

Used with seek() . Means the offset will be counted from the beginning of the file.