5.3.5 External I/O commands
The commands in this category are used to control and redirect
the generator output stream. The category also includes utility commands that
enable printing of diagrams and execution of external commands from within the
generators.
Filename write
By default, generator output is directed to a Generator Output
tool, from which it can be printed, saved to a file, or copied and pasted to
another application. Generator output can also be directed to a file, and
different parts of the output can be written to different files:
filename 'name.txt' write
/* generator output commands here */
close
Between
filename and
write there can be any generator
commands, all of whose output goes to building up the filename to be used. A
useful command here is prompt...ask, which opens a dialog asking the user for a
string, and then outputs that string: this then is used in building up the
filename, just as any output from any other keyword:
filename
prompt
'Enter file to output '
id
' to?'
ask
write
/* generator output commands here */
close
The filename can also include information about
directory paths.
All commands between write and close output to the file
specified. The close keyword immediately closes the output stream and writes the
file. Commands after close will send their output to whichever output stream was
in force before the filename command.
At the end of the whole generator a dialog will show how
many characters were written to each file. If an error occurs trying to write to
a file, the dialog will show the error message and output for that file will be
redirected to its own Generator Output tool.
Filename append
Append works like write with the exception that it does not
overwrite the old file if it already exists, but rather adds the generated
generator to the end of the file.
filename 'name.txt' append
/* generator output commands here */
close
To write several times to a file in a generator,
when it is impractical to keep the file open, you can open it the first time
with write (to overwrite any old version), and use append each time you open the
file later in the generator.
Filename merge with MD5 protected
block
To preserve hand-made changes in an output file use the
following template:
filename ...FILENAME... merge
...
md5id ...ID... md5block
...BODY...
md5sum
...
close
The generated output on file ...NAME... will
be:
...
/* MEPMD5 ...ID... */
...BODY...
/* MEPMD5 1234567890abcdef1234567890abcdef */
...
This output will be merged with the file ...NAME...
on disk. A block in the generator output will be replaced by the block on the
the disk if its ...ID... is the same and the body of the disk block has been
edited. The generator knows this because its MD5 checksum will not match the
checksum on disk.
Notes:
| The
start and stop tokens, /* MEPMD5 ... /*, must be unique in the file. Since
comment characters are different in different file types, the default tokens can
be changed for a file using the md5start and md5stop keywords: |
filename 'foo.txt'
[md5start '/* MEPMD5 ' ...]
[md5stop ' */' newline ...]
(merge|write|append)
...
close
If they are not explicitly declared, then they
default to the values above, even if there is no enclosing file
stream.
| The
body is converted to UTF-8 when calculating MD5. This is standard practicefor
MD5, and stops losing difference between e.g. x (0078 hex) and Y umlaut (0178
hex). |
| The
MD5 checksum is output as 32 lowercase hex digits, as in the RFC1321 test suite.
It is compared as a string, not a number, and the extra spaces at the start and
end are stripped
first. |
| A
block in a file counts as edited (and hence should be preserved) if it has a
non-empty MD5 string, and that does not match the MD5 string calculated for the
block. This allows you
to: |
| edit
just the MD5 string, e.g. to 0, to protect the current
block |
| edit
the contents of a block, and have that edit
preserved |
| delete
the MD5 part (or better, replace it with spaces, since normally there will be
two spaces anyway: /* MEPMD5 */) to mark that you no longer want that block to
be preserved, and the next generator run should overwrite
it. |
| Only
the merge variant of filename...close actually causes a merge, and is based
purely on the contents of its output stream and that on the disk not on whether
any MD5 commands were run directly in
it. |
| The
output tool shows the status 'written' if the file did not exist before,
'unchanged' if the file existed but would be unchanged by the merge result, and
'merged' if the file existed beforehand and was written now (whether or not any
blocks were
preserved). |
| The
output tool entry's contents are what was written to disk, i.e. the mixture of
model output and disk contents. |
Filename read
To open an existing plain text file and add its contents to
the generator output stream, use the template:
filename 'name.txt' read
If the named file is not
found, an error dialog is shown and generation is aborted. To ensure a file
exists, without overwriting or changing an existing file, you can use the
filename...append...close command with
no content.
Filename encoding
All text within MetaEdit+ is effectively
Unicode
, and you need not think about its encoding. Only
when reading and writing to files does the encoding matter. In most cases,
external programs will expect files to be in the local platform encoding or code
page, and so this is the encoding used by MetaEdit+ by default. Should you wish
to change the encoding, e.g. to write an XML file in UTF8 encoding, you can use
the
encoding parameter:
filename id '.xml' encoding 'utf-8' write
'<?xml version="1.0" encoding="UTF-8"?>'
...
close
The
encoding parameter is the first
parameter after the clauses whose output makes up the file name, and so comes
before md5start. The clauses between encoding and the next parameter of the
filename (e.g. md5start, write or read) go to make up the name of an encoding.
Most commonly this will just be a literal string like 'utf8' or a variable $enc
that was set earlier, but it can of course be any legal sequence of
clauses.
Encoders can be names like '437', 'cp437', 'ms_cp_437',
'ms-cp-437', 'ibm437' (all the same thing), 'iso-8859-1', 'iso8859-1',
'iso8859_1' (all the same thing), 'utf8', 'utf-8', 'utf_8', (all the same
thing), 'utf-16', 'ucs-2', 'ascii', 'jis', 'shiftjis', 'koi8-r', 'base64', and
'windows-1252'.
By using filename...encoding...read into a variable, then
filename...encoding...write, you can change the encoding of a file on
disk.
Note that encoding does not affect line breaks, which are
always read and written in the platform default way.
Filename print
The diagram for the current graph can be exported to the
printer, one of several graphics file types (.pct (PICT vector graphics), .gif,
.png), or a model export file (.mec, or if the API is installed, also .mxm)
using the Print template:
filename 'name.ext' print
If the file named is
'lpt1', the diagram is printed directly to the default printer, scaled to fit on
a single page. If the filename ends in .pct, .gif, or .png, a PICT, GIF or PNG
file is generated showing the whole diagram at 100%. If the filename ends in
.mec or .mxm, the graph (not just this diagram) will be exported to a MetaEdit+
binary patch file or XML models file. Again, all output between filename and
print keywords goes to building up the filename to export to.
For HTML files MetaEdit+ automatically creates imagemap
information to make GIF and PNG pictures clickable. When a
filename...print command is executed in
the generator, if the stream that is open after the print command is a file
named *.htm*, an image map will be written to that stream at the current
position. That position should thus not be inside any other tag; other than
that, it may be anywhere in the body of the HTML file.
The image map includes a polygon entry for each object,
based on the connection points of that object. The href parameter for each entry
is generated by an automatically run subgenerator, _imagemap_href. The default
for this is defined in Graph, and makes the link point to a local target named
with the oid of the object, e.g. #16_2876. It also uses onMouseOver and
onMouseOut to display the name and type of that object in the browser status
bar.
You may override the default _imagemap_href generator in
your own Graph types by making generators there with the same name. For
instance, Project Model defines its own such generator. Add the
USEMAP="#filename.gif" parameter to the IMG tag: the filename.gif should be
exactly the same as the SRC parameter, preceded by #.
External execute
External commands, e.g. compilers, can be executed using the
External template:
external 'javac ' id '.java' execute
Again, all
output between external and execute keywords goes to building up the command to
be executed. For example, the command above would run a Java compiler on a .java
file named after the current object, e.g. ‘javac Stopwatch.java’.
Commands are executed immediately they are encountered, and the generator
continues executing without waiting for the command to return.
To block generator execution during the external command,
substitute the final execute command
with an executeBlocking command,
i.e.
external 'javac ' id '.java' executeBlocking
Note
that executeBlocking can only work for
programs, i.e. something that exists as an executable (e.g. .exe, .com or .bat
on Windows); execute also works with
URLs, documents, and internal DOS commands such as
mkdir. To use
executeBlocking for non-programs, you
can make a command that invokes the platform’s command processor or a
script like metarun. E.g. the following
would work in Windows:
external 'start "" cmd /x /c mkdir NEW' executeBlocking
A
useful resource for command processors, batch files and shell scripts is
www.ss64.com.
Internal execute
To run the command stream as command-line arguments in this
MetaEdit+ session use:
internal 'forAll:run: WatchFamily Checkings' execute
The
example runs the Checkings generator on all instances of WatchFamily. There is
no use here for non-blocking execution, so the closing tags
execute and
executeBlocking behave
identically.
Path name separator
The separator character for path name components varies
between operating systems, e.g. ‘\’ in Windows but ‘/’
in Unix. To overcome this problem in generators aimed for cross-platform usage,
use the
sep
keyword to output the separator character for
the current platform.
Prompt ask
During a generator run a string can be prompted from the user
by using the Ask template:
prompt 'Enter a string' ask
This shows the user a
dialog with the specified prompt text. The text the user enters is then output
to the generator output, useful for example to name generated files. If the user
cancels the dialog, he is given the choice to abort the
generation.