gmslWriteClass

The Write Service Class

The Write service class contains the basic methods needed to write structured reports and text files of various sorts using binary information. It is used very extensively by gmBasic during every phase of its operation and is fully exposed via gmSL. Ultimately, at any given point in time, only one coded output report can be written at one time. Logically, a structured text output, be it a free-form text representation or a tabular representation containing fixed fields, is viewed as having four levels:

Level Description
File There are 5 totally independent file structures that may be in use. Initially only 1 of these, unit 1, is active. It is managed via the gmPL Output statement. Internally, gmBasic uses unit 5 as a scratch output file. This unit is never left open outside of a given task within the process. The other units are available as needed. (Note to tool implementers that there is a constant WRT_PROVIDERS, currently set to 5, that may be changed when doing a tool build. It is unit 1 and unit WRT_PROVIDERS whose use is restricted to gmBasic.) Each file has a physical file associated with it, which is either a named file or the Standard Output file. At any given point in time only one file unit is active, unit 1 is always the default, and all write operations are performed on that unit. The term selected unit is used to refer to the currently active one.
Stream By default records are written to the physical file associated with the selected unit; however, in many cases what is needed is an internal representation of what is to be written to the file. This representation, referred to as a text stream, can then be saved, edited, audited, and perhaps eventually physically written to a file. At any point in time, the selected unit is either writing to its physical file or to a text stream. During its life a file unit can spawn as many text streams as desired, but can only process one at a time.
Record Each file unit has a record buffer associated with it. This buffer stores the characters being written until it is physically written to the active stream or is disposed of in some other manner such as being copied to the runtime data queue. Low level methods in this service class all add their output to the record associated with the selected unit. The records in the different units are completely independent of each other. The maximum number of characters that may stored in a record at one time is 8192. (Note to tool implementers there is a constant MAX_BUFFER that defines this value.)
Field Ultimately each method that adds characters to a record is said to be "writing a field" at the back of the record of the selected unit. Characters can be entered into fields either free-form, or left-justified in a fixed-width field with a white space to the right, or right-justified in a fixed-width field with white space to the left.


Write_ChangeMargin Method

gmSL: void Write_ChangeMargin(int delta)
gmVB: Sub Write_ChangeMargin(ByVal delta As Integer)
gmNI: void Write_ChangeMargin(int delta);

When authoring gmBasic does not attempt to reproduce the leading white space in the source code; rather, it keeps track of the margin nesting level via the p and q escape characters in the surface patterns or via calls to this method and then uses this margin level times the indentation width specified via the Select Indent attribute to determine the number of leading blanks or tabs to add.

This method adds the value of delta to the current margin level. If delta is negative, the indentation will be decreased, and if it is positive, the indentation will be increased. If the computed margin level ends up being negative, it is set back to zero.

As an example, the following method displays unindented and indented lines.


 void DemoWriteChangeMargin(string comment)
 {
    Write_Line("Unindented Line: " + comment);
    Write_ChangeMargin(1);
    Write_Line("Indented Line: " + comment);
    Write_ChangeMargin(-1);
    Write_Line("Unindented Line: " + comment);
 }
Then the gmPL statements


 <Select Indent="3" />
 <Output Status="New" Filename="Demo001.out" />
 <RunCommand Id="scmCode.DemoWriteChangeMargin"  prams="Indent is selected at 3"/>
 <Select Indent="8" />
 <Output Status="Old" Filename="Demo001.out" />
 <RunCommand Id="scmCode.DemoWriteChangeMargin"  prams="Indent is selected at 8"/>
 <Select Indent="0" />
 <Output Status="Old" Filename="Demo001.out" />
 <RunCommand Id="scmCode.DemoWriteChangeMargin"  prams="Indent is selected at 0"/>
generate the following output


 Unindented Line: Indent is selected at 3
    Indented Line: Indent is selected at 3
 Unindented Line: Indent is selected at 3
 Unindented Line: Indent is selected at 8
         Indented Line: Indent is selected at 8
 Unindented Line: Indent is selected at 8
 Unindented Line: Indent is selected at 0
 	Indented Line: Indent is selected at 0
 Unindented Line: Indent is selected at 0
Note that the value of the selected indent is applied when the output file is opened. Using the Old status forces this open to occur while still writing to the back of the file.

Write_Character Method

gmSL: void Write_Character(int charValue)
gmVB: Sub Write_Character(ByVal charValue As Integer)
gmNI: void Write_Character (int charValue);

This method appends the ASCII character whose display code is specified by charValue to the current output record. No actual write occurs at this time.

Note that if called from gmSL with a string argument, the string is not converted to an integer as would be the normal convention; rather, the value of the first character in the string is passed.

As an example the following displays a character value


 Sub DemoWriteCharacter(ByVal theCharacter)
    Write_Text "Character Value is: "
    Write_Character(theCharacter)
    Write_Record
 End Sub
Note that the parameter theCharacter is not typed in the code; therefore, it will take on its type based on the type of the argument passed in as shown in the following


 <Output Status="New" Filename="Demo002.out" />
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="65"/>
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="F"/>
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="'65'"/>
which produces the following output


 Character Value is: A
 Character Value is: F
 Character Value is: 6
A is the character with display code 65; whereas the string '65' simply has its first character displayed.

Write_CloseFile Method

gmSL: void Write_CloseFile()
gmVB: Sub Write_CloseFile()
gmNI: void Write_CloseFile(void);

The Write_CloseFile method closes the selected unit and any physical file associated with that unit. Upon completion, unit 1, the unit associated with the gmPLOutput statement, becomes the selected unit.

The following code opens and writes records to three different files -- two files that it maintains under units 2 and 3 and the file associated with the Output statement. The important point made in this example are the two writes done after the file closings. It is these writes that go to the Output file.


 void DemoWriteSelect(string file1,string file2)
 {
    Write_OpenFile(2,file1,True,OutStyle.Text);
    Write_OpenFile(3,file2,True,OutStyle.Text);
    Write_Select(2);
    Write_Line("Line 1 in File " + file1);
    Write_Select(3);
    Write_Line("Line 1 in File " + file2);
    Write_Select(2);
    Write_Line("Line 2 in File " + file1);
    Write_CloseFile();
    Write_Line("Line 1 in Output File");
    Write_Select(3);
    Write_Line("Line 2 in File " + file2);
    Write_CloseFile();
    Write_Line("Line 2 in Output File");
}
The following gmPL statements then write output to three different files.


 <Output Status="New" Filename="Demo017.out" />
 <RunCommand Id="scmCode.DemoWriteSelect" prams="Demo017A.out;Demo017B.out" />
and the results are as expected. The file Demo017A.out contains


 Line 1 in File Demo017A.out
 Line 2 in File Demo017A.out
and file Demo017B.out contains


 Line 1 in File Demo017B.out
 Line 2 in File Demo017B.out
and file Demo017.out, the Output file contains


 Line 1 in Output File
 Line 2 in Output File

Write_CloseStream Method

gmSL: int Write_CloseStream()
gmVB: Function Write_CloseStream() As Integer
gmNI: int Write_CloseStream(void);

In gmBasic authored records are stored in text buffers within the current storage area. The CloseStream method closes the currently open text buffer and returns its integer handle. This handle must be saved by the caller to be used later to either reopen the text buffer or to physically write it to a file. If writes are currently being sent directly to the file specified in the Output statement, then this method does nothing.

The following code shows how text buffers as used.


 void DemoWriteTextStreams(string theAuthor)
 {
    int textRoot;
    Handle textStream;

    Write_Line("Write record to Output file before opening a text buffer");
    Write_OpenStream();
    #TextStart
    Hello World
       This is a message from (%= theAuthor %)
    GoodBye World
    #TextEnd
    textRoot = Write_CloseStream();
    Write_Line("Write another record then write content of text buffer");
    textStream = Text_Open(Store_GetHandle(),textRoot);
    Write_TextStream(textStream,0,0);
    Text_Close(textStream);
 }
The records both before and after the write to the text buffer go to the Output file before the buffer itself is written using the Write_TextStream() method. Note it is the return value from this method that is needed to open the text buffer for later use. This code then


 <Output Status="New" Filename="Demo003.out" />
 <RunCommand Id="scmCode.DemoWriteTextStreams"  prams="Kilroy"/>
produces the following output.


 Write record to Output file before opening a text buffer
 Write another record then write content of text buffer
 Hello World
    This is a message from Kilroy
 GoodBye World
Notice also that the #TextStart .. #TextEnd block behaves in the same manner as text written explicitly using the Write service class.

Write_Field Method

gmSL: void Write_Field(string strValue, int nString, int iField)
gmVB: Sub Write_Field(ByVal strValue As String, ByVal nString As Integer, ByVal iField As Integer)
gmNI: void Write_Field(char* strValue, int nString, int iField);

This method is the lowest level utility within the Write service class. It appends a specified field of characters to the current output record. No actual write occurs at this time. Within that field the content of strValue may be preceded or followed by white space. nString specifies the number of characters from strValue that are to be used. iField specifies how the characters from strValue should be entered:

Value Meaning
0 Simply append the characters to the current content of the output record.
+n Right justify the characters in a field n characters wide appended to the back of the current record.
-n Left justify the characters in a field n characters wide appended to the back of the current record.

Note that when called via gmSL if nString is set to zero, then the length of the string will be used.

The following code uses this method to display a string in various ways


 void DemoWriteField(string aLongString)
 {
    Write_Field(aLongString,0,0);
    Write_Record();
    Write_Field(aLongString,5,0);
    Write_Record();
    Write_Text("LEFT(");
    Write_Field(aLongString,10,-12);
    Write_Text(")RIGHT");
    Write_Record();
    Write_Text("LEFT(");
    Write_Field(aLongString,10,12);
    Write_Text(")RIGHT");
    Write_Record();
 }
When called as follows


 <Output Status="New" Filename="Demo004.out" />
 <RunCommand Id="scmCode.DemoWriteField"  prams="Kilroy was here"/>
it produces this output


 Kilroy was here
 Kilro
 LEFT(Kilroy was  )RIGHT
 LEFT(  Kilroy was)RIGHT

Write_Integer Method

gmSL: void Write_Integer(int iValue, int iField)
gmVB: Sub Write_Integer(ByVal iValue As Integer, ByVal iField As Integer)
gmNI: void Write_Integer(int iValue, int iField);

This method appends the base 10 display form of iValue in a specified field of characters to the current output record. No actual write occurs at this time. No special formatting flags are available. iField specifies how the characters of the display form should be entered:

Value Meaning
0 Simply append the characters to the current content of the output record.
+n Right justify the characters in a field n characters wide appended to the back of the current record.
-n Left justify the characters in a field n characters wide appended to the back of the current record.

The following code uses this method to display an integer value in various ways


 void DemoWriteInteger(int iValue)
 {
    Write_Text("LEFT(");
    Write_Integer(iValue,0);
    Write_Text(")RIGHT");
    Write_Record();
    Write_Text("LEFT(");
    Write_Integer(iValue,-10);
    Write_Text(")RIGHT");
    Write_Record();
    Write_Text("LEFT(");
    Write_Integer(iValue,10);
    Write_Text(")RIGHT");
    Write_Record();
 }
When called as follows


 <Output Status="New" Filename="Demo005.out" />
 <RunCommand Id="scmCode.DemoWriteInteger" prams="12345" />
it produces this output


 LEFT(12345)RIGHT
 LEFT(12345     )RIGHT
 LEFT(     12345)RIGHT

Write_Line Method

gmSL: void Write_Line(string strValue)
gmVB: Sub Write_Line(ByVal strValue As String)
gmNI: void Write_Line(char* strValue);

This method appends all characters from strValue to the current output record and then writes the result either to the currently open text buffer or to the file specified in the Output statement. No formatting is applied to the string.

The following code contrasts using a TextBlock versus using Write_Line() and Write_ChangeMargin().


 void DemoWriteLine(string theAuthor)
 {
    #TextStart
    Hello World
       This is a message from (%= theAuthor %)
    GoodBye World
    #TextEnd
    Write_Line("Hello World");
    Write_ChangeMargin(1);
    Write_Line("This is a message from " + theAuthor);
    Write_ChangeMargin(-1);
    Write_Line("GoodBye World");
 }
When called as follows


 <Output Status="New" Filename="Demo007.out" />
 <RunCommand Id="scmCode.DemoWriteLine" prams="Kilroy" />
it produces this output


 Hello World
    This is a message from Kilroy
 GoodBye World
 Hello World
    This is a message from Kilroy
 GoodBye World

Write_LogMessage Method

gmSL: void Write_LogMessage(string message)
gmVB: Sub Write_LogMessage(ByVal message As String)
gmNI: void Write_LogMessage(char* message)

The Write_LogMessage method writes a string message to the file. This is the same file used by gmBasic for its messages. The following gmPL script shows how this method might be used to debug a malfunctioning script.


 <gmBasic>
    <Storage Action="Create" />
    <gmsl>Write_LogSetName("Demo018.log")</gmsl>
 <gmsl>Write_LogMessage("<!-- translation options ")</gmsl>
    <Select progress="1" />
    <Select DevEnv="VS2010" />
    <Select Dialect="csh" />
    <Select BuildFile="global" />
 <gmsl>Write_LogMessage("<!-- directories for deployment and external assemblies -->")</gmsl>
    <Select DeployLocation="c:\gmProj\lab\deploy\externs\CrashV1003_csh" />
    <Select Library="c:\gmProj\lab\deploy\externs" />
    <select Target="c:\gmProj\lab\idf\FromCode" />
    <Select Local="c:\gmProj\lab\idf\FromCode" />
    <Select System="c:\gmProj\lab\idf\FromIdl" />
 <gmsl>Write_LogMessage("<!-- processing commands -->")</gmsl>
    <Compile Project="c:\gmSrc\GMTest\CrashV1003\CrashV1003.vbp" />
    <gmsl>Write_LogSetName(Null)</gmsl>
    <Analyse />
 <gmsl>Write_LogMessage("<!-- authoring commands -->")</gmsl>
    <Output Status="New" Filename="Demo018.bnd" />
    <Author />
    <Storage Action="Close" />
 <gmsl>Write_LogMessage("<!-- End of script -->")</gmsl>
 </gmBasic>
Note first of all, that gmSL command mode is being used in this script. Though the logging methods can be used in runtime code as desired for messages, they are also ideal for direct use within scripts. Running this script produces this Demo018.log file


 <!-- translation options
 <!-- directories for deployment and external assemblies -->
 <!-- processing commands -->
 Processing file: c:\gmSrc\GMTest\CrashV1003\CrashV1003.vbp
 Loading reference:[stdole2.tlb] c:\gmProj\lab\idf\FromIdl\stdole2.tlb.xml
 Loading reference:[scrrun.dll] c:\gmProj\lab\idf\FromIdl\scrrun.dll.xml
 Reprocessing file: c:\gmSrc\GMTest\CrashV1003\clsCrashV1003.cls
 Reprocessing file: c:\gmSrc\GMTest\CrashV1003\clsCrashV1003.cls
and then sends these remaining messages to Standard output.


 <!-- authoring commands -->
 <!-- End of script -->
Note that the messages written by this method coexist with the messages written by gmBasic and both use the same file.

Write_LogSetName

gmSL: void Write_LogSetName(string fileName)
gmVB: Sub Write_LogSetName(ByVal fileName As String)
gmNI: void Write_LogSetName(char* fileName)

The Write_LogSetName methods sets a fileName for the file to receive all messages -- progress, warning, error, etc. -- written during a given run. It also receives all messages written via the Write_LogMessage() method. By default log messages are simply written to the Standard Output file; however, when set to a named file, that file is opened for append access prior to each write and is then closed after the write. This ensures that no output sent to a named log file is lost in cases where abnormal exits are occurring. To set the log file back to using Standard Output pass it either Null or Nothing.

The following script reproduced from the discussion of the Write_LogMessage() shows these two types of calls.


 <gmBasic>
    <Storage Action="Create" />
 <gmsl>Write_LogSetName("Demo018.log")</gmsl>
    <gmsl>Write_LogMessage("<!-- translation options ")</gmsl>
    <Select progress="1" />
    <Select DevEnv="VS2010" />
    <Select Dialect="csh" />
    <Select BuildFile="global" />
    <gmsl>Write_LogMessage("<!-- directories for deployment and external assemblies -->")</gmsl>
    <Select DeployLocation="c:\gmProj\lab\deploy\externs\CrashV1003_csh" />
    <Select Library="c:\gmProj\lab\deploy\externs" />
    <select Target="c:\gmProj\lab\idf\FromCode" />
    <Select Local="c:\gmProj\lab\idf\FromCode" />
    <Select System="c:\gmProj\lab\idf\FromIdl" />
    <gmsl>Write_LogMessage("<!-- processing commands -->")</gmsl>
    <Compile Project="c:\gmSrc\GMTest\CrashV1003\CrashV1003.vbp" />
 <gmsl>Write_LogSetName(Null)</gmsl>
    <Analyse />
    <gmsl>Write_LogMessage("<!-- authoring commands -->")</gmsl>
    <Output Status="New" Filename="Demo018.bnd" />
    <Author />
    <Storage Action="Close" />
    <gmsl>Write_LogMessage("<!-- End of script -->")</gmsl>
 </gmBasic>

Write_NText Method

gmSL: void Write_NText(string strValue, int nText)
gmVB: Sub Write_NText(ByVal strValue As String, ByVal nText As Integer)
gmNI: void Write_NText(char* strValue, int nText);

This method appends the first nText characters from strValue to the current output record. No actual write occurs at this time. No formatting is applied to the string. Note that care must be taken that nText does not exceed the length of strValue. Since gmSL supports a string type, this method is rarely needed. It is intended primarily for use by gmNI.

The following code uses this method to display an a string value fully and in truncated form


 void DemoWriteNText(string aLongString)
 {
    Write_Text(aLongString);
    Write_Record();
    Write_NText(aLongString,5);
    Write_Record();
 }
When called as follows


 <Output Status="New" Filename="Demo006.out" />
 <RunCommand Id="scmCode.DemoWriteNText" prams="Kilroy was here" />
it produces this output


 Kilroy was here
 Kilro

Write_OpenFile Method

gmSL: void Write_OpenFile(int unit, string fileName,bool statusNew,gmsl.OutStyle syntax)
gmVB: Sub Write_OpenFile(ByVal Unit As Integer,ByVal fileName As String,
ByVal statusNew As Boolean,ByVal Syntax As Integer)
gmNI: void Write_OpenFile(int unit, char* fileName,int statusNew,int syntax);

There are 5 totally independent file structures that may be in use. The Write_OpenFilegmPL Output statement which uses unit 1.

The Unit parameter specifies which file unit is to be used. It must be a value between 1 and 5, though as the example below will show using unit 1 is equivalent to using the Output statement. The fileName parameter is the name of the file to be opened, and the statusNew parameter, if True request that a new file be created as opposed to opening an existing file for append access. The syntax parameter specifies the output style to be used when producing tabular reports. They can be produced in one of three styles: Tabbed simple tab delimited; Text text tabular; or Html html tabular.

The procedure below sows a simple method that could be used as a replacement for the gmPL Output statement.


 void DemoWriteOpenFile(string fileName, bool statusNew)
 {
    Write_OpenFile(1,fileName,statusNew,OutStyle.Text);
 }
and when used as follows


 <gmBasic>
 <Storage Action="Create" />
 <Select GlobalSettings="GmslCode.vbi" />
 <LoadEnvironment />
 <RunCommand Id="scmCode.DemoWriteOpenFile" prams="Demo016.out;.T." />
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="65"/>
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="F"/>
 <RunCommand Id="vbCode.DemoWriteCharacter"  prams="'65'"/>
 <Storage Action="Close" />
 </gmBasic>
produces the same output as the statement


 <Output Status="New" Filename="Demo016.out" />
would have produced.

Write_OpenStream Method

gmSL: void Write_OpenStream()
gmVB: Sub Write_OpenStream()
gmNI: void Write_OpenStream(void);

In gmBasic authored records are stored in text buffers within the current storage area. The OpenStream method creates such a text buffer and makes it the current output stream. All subsequent writes will go to this stream until it is closed. At which point the current output stream will revert to the file specified in the Output statement.

The following code shows how text buffers as used.


 void DemoWriteTextStreams(string theAuthor)
 {
    int textRoot;
    Handle textStream;

    Write_Line("Write record to Output file before opening a text buffer");
    Write_OpenStream();
    #TextStart
    Hello World
       This is a message from (%= theAuthor %)
    GoodBye World
    #TextEnd
    textRoot = Write_CloseStream();
    Write_Line("Write another record then write content of text buffer");
    textStream = Text_Open(Store_GetHandle(),textRoot);
    Write_TextStream(textStream,0,0);
    Text_Close(textStream);
 }
The records both before and after the write to the text buffer go to the Output file before the buffer itself is written using the Write_TextStream() method. This code then


 <Output Status="New" Filename="Demo003.out" />
 <RunCommand Id="scmCode.DemoWriteTextStreams"  prams="Kilroy"/>
produces the following output.


 Write record to Output file before opening a text buffer
 Write another record then write content of text buffer
 Hello World
    This is a message from Kilroy
 GoodBye World

Write_Pattern Method

gmSL: void Write_Pattern(string PatString, int nPattern, string Parameters)
gmVB: Sub Write_Pattern(ByVal PatString As String,ByVal nPattern As Integer, ByVal Parameters As String)
gmNI: void Write_Pattern(char* PatString,int nPattern,char* Parameters);

This method enters the pattern string PatString starting at the current location in into the current output record using editing directives within the string along with parameters defined in Parameters. nPattern contains the number of characters in PatString to be used. Parameters contains a semicolon-delimited string containing parameters referenced via their number. If the PatString contains no such references it may be empty. No actual write occurs at this time.

Directives within PatString are enclosed in % signs. The following are recognized:

Directive Meaning
%% Enter a percent sign
%USR_VERSION% Platform specified user version identifier
%PRM_VERSION% Platform specified system version identifier
%PRM_BUILDID% Platform build signature string
%DATE% Current date using currently selected formatting options
%TIME% Current time using currently selected formatting options
%envname% Value of environment variable envname. It must begin with an alphabetic character and may contain no more than 31 characters.
%nd Enter the nth (1-based) parameter string

Note that when called via gmSL if nPattern is set to zero, then the length of PatString will be used.

The following code shows how a pattern can be used.


 void DemoWritePattern(string what, string whom)
 {
    Write_Pattern("The event %1d happened at %TIME% on %DATE% to %2d", 0, what + ";" + Whom);
    Write_Record();
 }
As an example, the following commands


 <Output Status="New" Filename="Demo008.out" />
 <RunCommand Id="scmCode.DemoWritePattern" prams="Got Lost;Kilroy" />
produce the output


 The event Got Lost happened at 13:33:03 on 2013-01-02 to Kilroy
Note that in this case the use of semicolons within the RunCommand prams attribute interferes with the use of semicolons in the Parameters parameter here, so some care must be taken.

Write_Record Method

gmSL: void Write_Record()
gmVB: Sub Write_Record()
gmNI: void Write_Record(void);

This method completes the authoring of the current output record and then writes the result either to the currently open text buffer or to the file specified in the Output statement. The steps performed before the write include: first, special codes that are used to represent single and double quotes in either escaped or un escaped form must be converted to the final form required by the target language; second, if the echo translation flag is set then the current margin nesting level and the record are written to the log file; third, if the author is writing ASP code then the record must be checked for a transition into or out or VBSCRIPT; and fourth, if the record is longer that the Select MaxOutputWidth value then it is broken into multiple records using the conventions of the target language.

The following code contrasts using Write_Text() and Write_Record() with using Write_Line().


 void DemoWriteRecord(string theAuthor)
 {
    Write_Line("This is a message from " + theAuthor);
    Write_Text("This is a message from ");
    Write_Text(theAuthor);
    Write_Record();
    Write_Text("This is a message from " + theAuthor);
    Write_Record();
 }
Using this, the following commands


 <Output Status="New" Filename="Demo009.out" />
 <RunCommand Id="scmCode.DemoWriteRecord" prams="Kilroy" />
produce this set of identical lines


 This is a message from Kilroy
 This is a message from Kilroy
 This is a message from Kilroy

Write_Select Method

gmSL: int Write_Select(int unit)
gmVB: Function Write_Select(ByVal unit As Integer) As Integer
gmNI: int Write_Select(int unit);

The Write_Select method selects one the available file structures as the current unit. All successive write support will be performed by this unit until another call to this service is made or until the unit is closed. Note that this service only selects the unit it does not open its output channels or change it in any way. The unit parameter is the unit sequence number of the desired file structure. It must have a value between 1 and 5. Note that unit 1 is connected to the gmPL Output statement and should be used with care. Unit 5 is used by gmBasic as a temporary scratch unit -- not open between tasks -- and should only be used in the same way here.

The following code, reproduced from the discussion of Write_CloseFile() shows how this method can be used to flip back and forth between different files.


 void DemoWriteSelect(string file1,string file2)
 {
    Write_OpenFile(2,file1,True,OutStyle.Text);
    Write_OpenFile(3,file2,True,OutStyle.Text);
    Write_Select(2);
    Write_Line("Line 1 in File " + file1);
    Write_Select(3);
    Write_Line("Line 1 in File " + file2);
    Write_Select(2);
    Write_Line("Line 2 in File " + file1);
    Write_CloseFile();
    Write_Line("Line 1 in Output File");
    Write_Select(3);
    Write_Line("Line 2 in File " + file2);
    Write_CloseFile();
    Write_Line("Line 2 in Output File");
}

Write_SetIndentation Method

gmSL: void Write_SetIndentation(int indent, int offset)
gmVB: Sub Write_SetIndentation(ByVal indent As Integer, ByVal offset As Integer)
gmNI: void Write_SetIndentation(int indent, int offset);

This method sets the indentation widths for the current output stream. There are two width values: indent which specifies the number of spaces to indent per indentation level (a value of zero means use tabs); and offset which is used when a record is longer than the desired line-width. The following line can be set off this many additional spaces.

When an output file is first created, the default indent value is the Select Indent attribute and the default offset value is zero. When an text buffer is opened the initial values are the values from the current output file. The values specified here take immediate effect and can be changed within the same write sequence.

Consider the following code that writes an un indented line and then an indented one


 void DemoWriteSetIndentation(string comment, int indent)
 {
    Write_SetIndentation(indent,0);
    Write_Line("Unindented Line: " + comment);
    Write_ChangeMargin(1);
    Write_Line("Indented Line: " + comment);
    Write_ChangeMargin(-1);
 }
when called multiple times using various indent values


 <Output Status="New" Filename="Demo010.out" />
 <RunCommand Id="scmCode.DemoWriteSetIndentation" prams="Hello World;8" />
 <RunCommand Id="scmCode.DemoWriteSetIndentation" prams="Hello World;1" />
 <RunCommand Id="scmCode.DemoWriteSetIndentation" prams="Hello World;(%= Select.Indent %)" />
produces the following output


 Unindented Line: Hello World
         Indented Line: Hello World
 Unindented Line: Hello World
  Indented Line: Hello World
 Unindented Line: Hello World
    Indented Line: Hello World
The quantity Select.Indent sets the indentation level back to its original value.

Write_TableCell Method

gmSL: void Write_TableCell()
gmVB: Sub Write_TableCell()
gmNI: void Write_TableCell(void);

In many complex output situations it is often desirable to nest the output streams so that complex outputs can be nested within each other or can be constructed with independent sets of logic. This method allows for the writing of complex data cells within data tables. Rather than writing the current output record and thus clearing its internal buffers so that another record can be written, this method pushes the content of the current output record onto the runtime data queue and then clears it. Once a series of cells have been written, then the Write_TableRow() method writes a data row to the currently active output stream using the form required by the Output Syntax attribute set for it.

This method writes a table whose data cells contain information about all of the user defined variables in a completed translation.


 Sub DemoWriteTable()
    Dim iRoot As Integer
    Dim levels(19) As Integer

    DataQueue_SetBottom()
    Write_Text "Audit of Variables in " & Store_GetDataName() & " storage area"
    Write_TableCell()
    Write_TableTitle()
    DataQueue_PushString("Lev")
    DataQueue_PushInteger(3)
    DataQueue_PushString("Address")
    DataQueue_PushInteger(8)
    DataQueue_PushString("Full Symbol Identifier")
    DataQueue_PushInteger(-100)
    Write_TableHeadings()

    iRoot = Store_FindFirstChild(levels,0)
    Do while(iRoot <> 0)
       If Store_GetObjectType(iRoot) = ObjectType.VARIABLE Then
          Write_Integer(levels(0) - 1, 0)
          Write_TableCell()
          Write_Integer(iRoot,0)
          Write_TableCell()
          Write_Text(Symbol_FullName(iRoot,-1))
          Write_TableCell()
          Write_TableRow()
       End If
       iRoot = Store_FindNextChild(levels)
    Loop
    Write_TableEnd()
    DataQueue_ClearBottom(1)
 End Sub
A gmPL script that might use this method is as follows


  <gmBasic>
  <Storage Action="Open" Identifier="fmstock1.vbi" />
  <Select GlobalSettings="GmslCode.vbi" />
  <LoadEnvironment />
  <Output Status="New" Filename="Demo011.out" stripTrail="on" />
  <RunCommand Id="vbCode.DemoWriteTable" />
  <Storage Action="Close" />
  </gmBasic>
By convention all of the sample codes are compiled into a global settings file GmslCode.vbi. The class within that file which contains VB code is called vbCode. The fmstock1.vbi file is as it was produced within the FMSTOCK sample code. Nothing special was done to make it usable here. The Open action opens that file with Read Only permission. An abstract of the output follows.


  Audit of Variables in fmstock1.vbi storage area:
  Lev |  Address | Full Symbol Identifier
  --- |  ------- | ----------------------
    4 |   227293 | FMStocks_DB.Helpers.RegisterEventSource.lpUNCServerName
    4 |   227382 | FMStocks_DB.Helpers.RegisterEventSource.lpSourceName
    4 |   232756 | FMStocks_DB.Helpers.DeregisterEventSource.hEventLog
       ...
    3 |   253093 | FMStocks_DB.logDebugMSG.strSource
    3 |   253123 | FMStocks_DB.logDebugMSG.strMSG
    2 |   264958 | FMStocks_DB.COMSVCSLib_AppServer
The actual cells written by the Write_TableCell method are the individual entries in each row of the table.

Write_TableEnd Method

gmSL: void Write_TableEnd()
gmVB: Sub Write_TableEnd()
gmNI: void Write_TableEnd(void);

This method ends the authoring of a table of values. If the Output Syntax is Html then the Html closing tags are written; else this method does nothing.

Using the DemoWriteTable() subroutine with this script


 <gmBasic>
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Select GlobalSettings="GmslCode.vbi" />
 <LoadEnvironment />
 <Output Status="New" Filename="Demo012.html" Syntax="html" />
 <RunCommand Id="vbCode.DemoWriteTable" />
 <Output Status="Close" />
 <Storage Action="Close" />
 </gmBasic>
generates an output table with records like the following


 <html>
   ...
 <center><p><h4>Audit of Variables in fmstock1.vbi storage area</h4></p></center>
 <center><table cellpadding="2" cellspacing="0" bordercolor="#5B5F8B" border="1">
 <tr>
 <th rowspan="1" colspan="1" class="spanner" nowrap="nowrap">Lev</th>
 <th rowspan="1" colspan="1" class="spanner" nowrap="nowrap">Address</th>
 <th rowspan="1" colspan="1" class="spanner" nowrap="nowrap">Full Symbol Identifier</th>
 </tr>
 <tr>
 <td align="right" class="cell-odd" nowrap="nowrap">4</td>
 <td align="right" class="cell-odd" nowrap="nowrap">227293</td>
 <td align="left" class="cell-odd" nowrap="nowrap">FMStocks_DB.Helpers.RegisterEventSource.lpUNCServerName</td>
 </tr>
   ...
 <tr>
 <td align="right" class="cell-odd" nowrap="nowrap">2</td>
 <td align="right" class="cell-odd" nowrap="nowrap">264958</td>
 <td align="left" class="cell-odd" nowrap="nowrap">FMStocks_DB.COMSVCSLib_AppServer</td>
 </tr>
 </table></center>
 </body>
 </html>
In this output the lines at the top before the title and those below the end of the table are produced by the gmPL Output statement. It is the closing table and center entries that are made by the Write_TableEnd() method. When writing code like DemoWriteTable() it is important to remember that its actual output form will be determined at gmPL RunCommand time so always include calls to methods like Write_TableEnd() in case the caller decides to use Html syntax.

Write_TableHeadings Method

gmSL: void Write_TableHeadings()
gmVB: Sub Write_TableHeadings()
gmNI: void Write_TableHeadings(void);

This method writes a column heading to the currently active output stream using the form required by the Output Syntax attribute set for it. The column headings themselves and their desired widths are stored in the runtime data queue. Reviewing again the DemoWriteTable() subprogram


 Sub DemoWriteTable()
    Dim iRoot As Integer
    Dim levels(19) As Integer

    DataQueue_SetBottom()
    Write_Text "Audit of Variables in " & Store_GetDataName() & " storage area"
    Write_TableCell()
    Write_TableTitle()
    DataQueue_PushString("Lev")
    DataQueue_PushInteger(3)
    DataQueue_PushString("Address")
    DataQueue_PushInteger(8)
    DataQueue_PushString("Full Symbol Identifier")
    DataQueue_PushInteger(-100)
    Write_TableHeadings()

    iRoot = Store_FindFirstChild(levels,0)
    Do while(iRoot <> 0)
       If Store_GetObjectType(iRoot) = ObjectType.VARIABLE Then
          Write_Integer(levels(0) - 1, 0)
          Write_TableCell()
          Write_Integer(iRoot,0)
          Write_TableCell()
          Write_Text(Symbol_FullName(iRoot,-1))
          Write_TableCell()
          Write_TableRow()
       End If
       iRoot = Store_FindNextChild(levels)
    Loop
    Write_TableEnd()
    DataQueue_ClearBottom(1)
 End Sub
It is the DataQueue service class calls that set up the call to Write_TableHeadings which expects the find a series of (String, Integer) pairs in the data queue. The number of pairs defines the number of columns in the table, each string entry defines the column heading, and the integer entry defines the field width and orientation to the used as follows:

Value Meaning
+n Right justify the characters in a cell n characters wide.
-n Left justify the characters in a cell n characters wide.

These values are actually only used when the Output Syntax is Text; however, since this syntax choice does not take effect until gmPL runtime, the values should always be supplied with reasonable values.

Write_TableRow Method

gmSL: void Write_TableRow()
gmVB: Sub Write_TableRow()
gmNI: void Write_TableRow(void);

This method writes a data row to the currently active output stream using the form required by the Output Syntax attribute set for it. The cell content making up the data row are stored as strings on the runtime data queue. Reviewing again the DemoWriteTable() subprogram


 Sub DemoWriteTable()
    Dim iRoot As Integer
    Dim levels(19) As Integer

    DataQueue_SetBottom()
    Write_Text "Audit of Variables in " & Store_GetDataName() & " storage area"
    Write_TableCell()
    Write_TableTitle()
    DataQueue_PushString("Lev")
    DataQueue_PushInteger(3)
    DataQueue_PushString("Address")
    DataQueue_PushInteger(8)
    DataQueue_PushString("Full Symbol Identifier")
    DataQueue_PushInteger(-100)
    Write_TableHeadings()

    iRoot = Store_FindFirstChild(levels,0)
    Do while(iRoot <> 0)
       If Store_GetObjectType(iRoot) = ObjectType.VARIABLE Then
          Write_Integer(levels(0) - 1, 0)
          Write_TableCell()
          Write_Integer(iRoot,0)
          Write_TableCell()
          Write_Text(Symbol_FullName(iRoot,-1))
          Write_TableCell()
          Write_TableRow()
       End If
       iRoot = Store_FindNextChild(levels)
    Loop
    Write_TableEnd()
    DataQueue_ClearBottom(1)
 End Sub
The individual calls to Write_TableRow() assume that they have been preceded by as many calls to Write_DataCell() as there were columns defined in the table via the DataQueue calls and the final call to Write_TableHeadings(). The actual output produced by this method is determined at gmPL runtime depending upon the setting of the Output Syntax attribute. Previous topics have shown the output for Text (see Write_TableCell()) and Html (see Write_TableEnd()). The script here uses Tabbed syntax.


 <gmBasic>
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Select GlobalSettings="GmslCode.vbi" />
 <LoadEnvironment />
 <Output Status="New" Filename="Demo013.out" Syntax="Tabbed" />
 <RunCommand Id="vbCode.DemoWriteTable" />
 <Output Status="Close" />
 <Storage Action="Close" />
 </gmBasic>
The following is an abstract of the output produced, note that the white space is produced by the tabs.


 Audit of Variables in fmstock1.vbi storage area
 Lev	Address	Full Symbol Identifier
 4	227293	FMStocks_DB.Helpers.RegisterEventSource.lpUNCServerName
 4	227382	FMStocks_DB.Helpers.RegisterEventSource.lpSourceName
 4	232756	FMStocks_DB.Helpers.DeregisterEventSource.hEventLog
       ...
 3	253093	FMStocks_DB.logDebugMSG.strSource
 3	253123	FMStocks_DB.logDebugMSG.strMSG
 2	264958	FMStocks_DB.COMSVCSLib_AppServer
Regardless of the output syntax used, it is implemented at runtime when the various Write_Table methods are used. It is important to remember this when writing code that uses them.

Write_TableTitle Method

gmSL: void Write_TableTitle()
gmVB: Sub Write_TableTitle()
gmNI: void Write_TableTitle(void);

This method writes a title for a table of values along with any other control information that may be needed to begin the actual table display to the currently active output stream using the form required by the Output Syntax attribute set for it. The title itself is stored on the runtime data queue prior to the call to this method. The easiest way to do this is via the Write_TableCell() method though the DataQueue_PushString() could be used as well. Reviewing again the DemoWriteTable() subprogram,


 Sub DemoWriteTable()
    Dim iRoot As Integer
    Dim levels(19) As Integer

    DataQueue_SetBottom()
    Write_Text "Audit of Variables in " & Store_GetDataName() & " storage area"
    Write_TableCell()
    Write_TableTitle()
       ....
the actual form of the output depends upon the Output Syntax attribute. For Text a colon is added to the back of the title string.


 Audit of Variables in fmstock1.vbi storage area:
For tabbed the title string is unadorned.


 Audit of Variables in fmstock1.vbi storage area
For Html it is displayed as an h4 centered heading.


 <center><p><h4>Audit of Variables in fmstock1.vbi storage area</h4></p></center>

Write_Text Method

gmSL: void Write_Text(string strValue)
gmVB: Sub Write_Text(ByVal strValue As String)
gmNI: void Write_Text(char* strValue);

This method appends all characters from strValue to the current output record. No actual write occurs at this time. No formatting is applied to the string; however, the engine supporting gmSL performs concatenation and automatic conversions to string when processing addition within contexts that expect string arguments. For example the following code uses Write_Text


 void DemoWriteText(int opcValue)
 {
    tOpcInfo opcInfo;

    opcInfo = Opcode_GetInfo(opcValue);
    Write_Text("The Opcode " + opcInfo.ident + " has value " + opcInfo.value);
    Write_Record();
    Write_Text("   length    = " + opcInfo.length);
    Write_Record();
    Write_Text("   ident     = " + opcInfo.ident);
    Write_Record();
    Write_Text("   value     = " + opcInfo.value);
    Write_Record();
    Write_Text("   type      = " + Symbol_NamedEntryLabel("opcTypes",opcInfo.type));
    Write_Record();
    Write_Text("   role      = " + Symbol_NamedEntryLabel("opcRoles",opcInfo.role));
    Write_Record();
 }
to describe operation code properties. When called as follows


 <Output Status="New" Filename="Demo014.out" />
 <RunCommand Id="scmCode.DemoWriteText"  prams="5"/>
 <RunCommand Id="scmCode.DemoWriteText"  prams="13"/>
 <RunCommand Id="scmCode.DemoWriteText"  prams="170"/>
the following output is produced.


 The Opcode LIC has value 5
    length    = 17
    ident     = LIC
    value     = 5
    type      = ShortValue
    role      = LoadInt
 The Opcode ADD has value 13
    length    = 17
    ident     = ADD
    value     = 13
    type      = Arithmetic
    role      = Binary
 The Opcode USC has value 170
    length    = 17
    ident     = USC
    value     = 170
    type      = DoubleByte
    role      = Clsref

Write_TextStream Method

gmSL: void Write_TextStream(Handle textStream, int host, int margin)
gmVB: Sub Write_TextStream(ByVal textStream As Handle, ByVal host As Integer, ByVal margin As Integer)
gmNI: void Write_TextStream(void* textStream,int host, int margin)

This method writes the content of a text stream buffer to the current output file. The textStream parameter is obtained from one of the Text service class methods Text_Open() or Text_Create(). It is the handle to the text stream buffer to be written. The host parameter is the root offset of the symbol to which the text stream belongs. It may simply be zero. If nonzero, it is used to log this message


 SYSERR#6067: Text storage malformed for <identifier of symbol>
in case the text stream is malformed. The margin parameter is the starting indentation margin to be used while writing the text stream. When gmBasic writes text streams it does not insert any leading white space to show marginal indentation; rather it simply records the margin level for each line. It is when the text stream is actually written that the margin level times the indentation width white space is actually applied to the front of the record. When doing this, the value of margin is added to the margin value recorded for each line.

The following method shows how the translation associated with a class file can be authored.


 void DemoAuthorTextStream(int classRoot)
 {
    tInfoFile classFile;
    Handle    textStream;

    classFile = Store_GetVector(classRoot);
    textStream = Text_Open(Store_GetHandle(),classFile.tranBase);
    Write_TextStream(textStream,classRoot,0);
    Text_Close(textStream);
 }
In the following script


 <gmBasic>
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Select GlobalSettings="GmslCode.vbi" />
 <LoadEnvironment />
 <Output Status="New" Filename="Demo015.out" />
 <RunCommand Id="vbCode.DemoAuthorTextStream"
    prams="(%= Symbol_FindIdentifier("[C:\gmSrc\fmstocks\vb6\FMStocks_DB\Version.cls]",0) %)" />
 <Storage Action="Close" />
 </gmBasic>
the gmPL preprocessor executes the Symbol_FindIdentifier method which returns the root offset of the specified symbol and inserts it in string form in the prams attribute. The RunCommand then passes that root offset as an integer value to the DemoAuthorTextStream() method. That method then writes the translation associated with the class just as it was produced when the actual translation script was executed. Here is an abstract of that output


 cat >C:\gmProj\FmStocks\Deploy\FmStock1_csh\Version.cs <<'!)(!'
 using System;
 using System.Drawing;
   ...
 namespace FMStocks_DB
 {
    public class Version
    {
       private const string m_modName = "FMStocks_DB.Version";
       public string ConnectionString()
       {
          string ConnectionString = "";

          DBHelper dbh = null;
          try
          {
             MigrationSupport.Utils.ClearErrorObject();
             dbh = new FMStocks_DB.DBHelper();
             ConnectionString = dbh.GetConnectionString();
             dbh = null;
             return ConnectionString;

          }
          catch(Exception exc)
          {
             MigrationSupport.Utils.SetErrorObject(exc);
             Helpers.RaiseError(m_modName,"ConnectionString()","",0,false);
          }
          return ConnectionString;
       }
    ...
 }
 !)(!

Table of Contents