Up Previous Next Title Page Index Contents

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.

Up Previous Next Title Page Index Contents