text:
Visual Basic/Jarithmetic Round Two Implementation

=Introduction= This chapter describes an implementation of what has been discussed so far.

The technique used is to intersperse code and discussion. You should be able to extract the code by simply copying the whole page and commenting out the discussion.

The previous discussion has been at a very high level, implementing it will require both high level and low level coding and also a lot of refinement of our ideas.

The application will consist of a form, some modules and some classes. We'll begin at the top by creating the user interface of the application, then we will add the code that makes it work piece by piece. We will find that some of what was said in the previous discussion was incomplete, some of it misleading. That's what happens in real development.

=User Interface=

I have chosen to implement this program as a Multiple Document Interface application. this is usually referred to as a MDI application. All this means is that it will be possible to have more than one Jarithmetic document open at the same time in the same instance of the program. This is the way that most Microsoft Office applications worked in the past.

fMainform.frm
Here is a possible main form. The picture shows more menus than are actually implemented in the first version, implement them as you go along.

Here is the declaration of the controls on the form. You can paste this into a text editor and save it as fMainForm.frm to get started quickly.

The main form is a container for as many documents as the user wants to open. Each document will be an instance of frmDocument, see the next section.

VERSION 5.00 Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0"; "COMDLG32.OCX" Begin VB.MDIForm fMainform BackColor      =   &H8000000C& Caption        =   "Arithmetic" ClientHeight   =   2790 ClientLeft     =   165 ClientTop      =   765 ClientWidth    =   5280 Icon           =   "Fmainform.frx":0000 LinkTopic      =   "MDIForm1" StartUpPosition =  3  'Windows Default Begin MSComDlg.CommonDialog CommonDialog1 Left           =   360 Top            =   240 _ExtentX       =   847 _ExtentY       =   847 _Version       =   393216 End Begin VB.Menu mnuFile Caption        =   "&File" Index          =   1 Begin VB.Menu mnuFileNew Caption        =   "&New" Shortcut       =   ^N End Begin VB.Menu mnuFileOpen Caption        =   "&Open..." Shortcut       =   ^O End Begin VB.Menu mnuFileBar0 Caption        =   "-" End Begin VB.Menu mnuFileSave Caption        =   "&Save" Shortcut       =   ^S End Begin VB.Menu mnuFileSaveAs Caption        =   "Save &As..." End Begin VB.Menu mnuFileSaveAll Caption        =   "Save A&ll" End Begin VB.Menu mnuFileClose Caption        =   "&Close" Shortcut       =   ^E End Begin VB.Menu mnuFileCloseAll Caption        =   "&CloseAll" End Begin VB.Menu mnuFileBar1 Caption        =   "-" End Begin VB.Menu mnuFilePrint Caption        =   "&Print..." Shortcut       =   ^P End Begin VB.Menu mnuFilePrintSetup Caption        =   "&PrintSetup" End Begin VB.Menu mnuFilePrintPreview Caption        =   "&PrintPreview" Shortcut       =   ^R End Begin VB.Menu mnuFileBar3 Caption        =   "-" End Begin VB.Menu mnuFileSend Caption        =   "&Send" Begin VB.Menu mnuFileSendEmail Caption        =   "&Email" End End Begin VB.Menu mnuFileExit Caption        =   "E&xit" Shortcut       =   {F4} End End Begin VB.Menu mnuEdit Caption        =   "&Edit" Begin VB.Menu mnuEditUndo Caption        =   "&Undo" Shortcut       =   ^Z End Begin VB.Menu mnuEditRedo Caption        =   "&Redo" End Begin VB.Menu mnueditbar2 Caption        =   "-" End Begin VB.Menu mnuEditCut Caption        =   "Cu&t" Shortcut       =   ^X End Begin VB.Menu mnuEditCopy Caption        =   "&Copy" Shortcut       =   ^C End Begin VB.Menu mnuEditPaste Caption        =   "&Paste" Shortcut       =   ^V End Begin VB.Menu mnueditbar3 Caption        =   "-" End Begin VB.Menu mnuEditSelectAll Caption        =   "&SelectAll" Shortcut       =   ^A End End Begin VB.Menu mnuData Caption        =   "&Data" Begin VB.Menu mnuEvaluate Caption        =   "&Evaluate" Shortcut       =   {F9} End End Begin VB.Menu mnuWindow Caption        =   "&Window" WindowList     =   -1  'True Begin VB.Menu mnuWindowNewWindow Caption        =   "&New Window" Shortcut       =   {F12} End Begin VB.Menu mnuWindowBar0 Caption        =   "-" End Begin VB.Menu mnuWindowCascade Caption        =   "&Cascade" End Begin VB.Menu mnuWindowTileHorizontal Caption        =   "Tile &Horizontal" End Begin VB.Menu mnuWindowTileVertical Caption        =   "Tile &Vertical" End Begin VB.Menu mnuWindowArrangeIcons Caption        =   "&Arrange Icons" End End Begin VB.Menu mnuHelp Caption        =   "&Help" Begin VB.Menu mnuHelpContents Caption        =   "&HelpContents" Shortcut       =   {F1} End Begin VB.Menu mnuHelpTipoftheDay Caption        =   "&TipoftheDay" End Begin VB.Menu mnuHelpAbout Caption        =   "&About " End Begin VB.Menu mnuHelpSpecialThanks Caption        =   "&SpecialThanks" End End End Attribute VB_Name = "fMainform" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False

Now here is the visible code of the form. It is quite short as the MDI form doesn't do very much, it mostly just acts as a container for document forms.

The MDI form has a file menu. When there are no document forms open it is this menu that is active. When document forms are open the file menu that is shown belongs to the document form but we will still call this method. When VB creates the mthod for us it will be marked Private, we can change this to Public as has been done here but it might be cleaner and less confusing to leave it Private and add a new Friend method that calls it. When we open a document we must first create a document form to hold it. Within this program instance of frmDocument represent the document. The design decision made here is that each time we open a document it will be loaded into a new instance of frmDocument. This might or might not be appropriate, consider what happens if the user opens the same document twice and edits both. We use the Common Dialog control provided by Microsoft but we could also use a form containing Drive, Folder and File list controls. Option Explicit ' always use this to ensure that you don't forget to declare variables Public Sub mnuFileOpen_Click Dim oForm As frmDocument Set oForm = LoadNewDoc With CommonDialog1 ' The title should probably say something meaningful about the application and the document .DialogTitle = "Open" type .CancelError = False .Filter = gsFILE_FILTER .ShowOpen If Len(.FileName) = 0 Then Exit Sub End If     If Not oForm.LoadFile(.FileName) Then MsgBox "Could not load file <" & .FileName & ">," & vbCrLf & "probably couldn't find the zlib.dll.", _ vbOKOnly + vbCritical, Title End If   End With End Sub

The LoadNewDoc function is separated from the File Open event handler so that it can be used by other callers.

Public Function LoadNewDoc As frmDocument Static lDocumentCount As Long lDocumentCount = lDocumentCount + 1 Set LoadNewDoc = New frmDocument LoadNewDoc.Caption = "Document " & lDocumentCount LoadNewDoc.Show End Function

When we unload the main form we want to be able to make sure that all the documents are properly cleaned up so we call a function to exit from the application. We could use the return value to set the Cancel argument, then the user might be able to stop the shutdown.

Private Sub MDIForm_Unload(Cancel As Integer) ExitApplication End Sub

frmDocument.frm
The document form holds one of our arithmetic documents. It is not much more complicated than the main form. However it does define some different menus. Also because of the way VB6 works it must define all the same menus too, it can't inherit them from the main form. VB shows the menus defined by the current form unless there is only the MDI form in which case that one is shown. Again the graphic shows more menus than are actually implemented in this prototype.

Here are the definitions of the menus and controls:

VERSION 5.00 Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0"; "RICHTX32.OCX" Begin VB.Form frmDocument Caption        =   "Document" ClientHeight   =   3600 ClientLeft     =   60 ClientTop      =   60 ClientWidth    =   6225 Icon           =   "frmDocument.frx":0000 KeyPreview     =   -1  'True LinkTopic      =   "Form1" MDIChild       =   -1  'True ScaleHeight    =   3600 ScaleWidth     =   6225 WindowState    =   2  'Maximized Begin RichTextLib.RichTextBox rtfBox Height         =   3315 Left           =   120 TabIndex       =   0 Top            =   240 Width          =   6000 _ExtentX       =   10583 _ExtentY       =   5847 _Version       =   393217 Enabled        =   -1  'True HideSelection  =   0   'False ScrollBars     =   2 TextRTF        =   $"frmDocument.frx":030A BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851} Name           =   "Times New Roman" Size           =   12 Charset        =   0 Weight         =   400 Underline      =   0   'False Italic         =   0   'False Strikethrough  =   0   'False EndProperty End Begin VB.Menu mnuFile Caption        =   "&File" Index          =   1 Begin VB.Menu mnuFileNew Caption        =   "&New" Shortcut       =   ^N End Begin VB.Menu mnuFileOpen Caption        =   "&Open..." Shortcut       =   ^O End Begin VB.Menu mnuFileBar0 Caption        =   "-" End Begin VB.Menu mnuFileSave Caption        =   "&Save" Shortcut       =   ^S End Begin VB.Menu mnuFileSaveAs Caption        =   "Save &As..." End Begin VB.Menu mnuSaveCompressed Caption        =   "Save &Compressed" End Begin VB.Menu mnuFileSaveAll Caption        =   "Save A&ll" End Begin VB.Menu mnuFileClose Caption        =   "&Close" Shortcut       =   ^E End Begin VB.Menu mnuFileCloseAll Caption        =   "&CloseAll" End Begin VB.Menu mnuFileBar1 Caption        =   "-" End Begin VB.Menu mnuFilePrint Caption        =   "&Print..." Shortcut       =   ^P End Begin VB.Menu mnuFilePrintSetup Caption        =   "&PrintSetup" End Begin VB.Menu mnuFilePrintPreview Caption        =   "&PrintPreview" Shortcut       =   ^R End Begin VB.Menu mnuFileBar3 Caption        =   "-" End Begin VB.Menu mnuFileSend Caption        =   "&Send" Begin VB.Menu mnuFileSendEmail Caption        =   "&Email" End End Begin VB.Menu mnuFileExit Caption        =   "E&xit" Shortcut       =   {F4} End End Begin VB.Menu mnuEdit Caption        =   "&Edit" Begin VB.Menu mnuEditUndo Caption        =   "&Undo" Shortcut       =   ^Z End Begin VB.Menu mnuEditRedo Caption        =   "&Redo" End Begin VB.Menu mnueditbar2 Caption        =   "-" End Begin VB.Menu mnuEditCut Caption        =   "Cu&t" Shortcut       =   ^X End Begin VB.Menu mnuEditCopy Caption        =   "&Copy" Shortcut       =   ^C End Begin VB.Menu mnuEditPaste Caption        =   "&Paste" Shortcut       =   ^V End Begin VB.Menu mnueditbar3 Caption        =   "-" End Begin VB.Menu mnuEditSelectAll Caption        =   "&SelectAll" Shortcut       =   ^A End Begin VB.Menu mnuEditPloticus Caption        =   "Ploticus" End End Begin VB.Menu mnuView Caption        =   "&View" Begin VB.Menu mnuViewToolbar Caption        =   "&Toolbar" Checked        =   -1  'True End Begin VB.Menu mnuViewStatusBar Caption        =   "Status &Bar" Checked        =   -1  'True End Begin VB.Menu mnuViewRuler Caption        =   "&Ruler" Checked        =   -1  'True End End Begin VB.Menu mnuFormat Caption        =   "F&ormat" Begin VB.Menu mnuFormatFont Caption        =   "&Font..." End Begin VB.Menu mnuFormatColor Caption        =   "&Color..." End Begin VB.Menu mnuFormatBullet Caption        =   "&Bullet" End Begin VB.Menu mnuFormatTabs Caption        =   "&Tabs..." End Begin VB.Menu mnuFormatParagraph Caption        =   "&Paragraph" Begin VB.Menu mnuParagraphLeft Caption        =   "&Left Justified" End Begin VB.Menu mnuParagraphCentred Caption        =   "&Centred" End Begin VB.Menu mnuParagraphRight Caption        =   "&Right Justified" End End Begin VB.Menu mnuTypestyle Caption        =   "&Typestyle" Begin VB.Menu mnuBold Caption        =   "&Bold" Shortcut       =   ^B End Begin VB.Menu mnuItalic Caption        =   "&Italic" Shortcut       =   ^I End Begin VB.Menu mnuUnderline Caption        =   "&Underline" Shortcut       =   ^U End End Begin VB.Menu mnuformatfilebar1 Caption        =   "-" End Begin VB.Menu mnuFormatChangeCase Caption        =   "&ChangeCase" Begin VB.Menu mnuFormatChangeCaseLowerCase Caption        =   "&LowerCase" End Begin VB.Menu mnuFormatChangeCaseUpperCase Caption        =   "&UpperCase" End End Begin VB.Menu mnuFormatFilebar2 Caption        =   "-" End Begin VB.Menu mnuFormatIncreaseIndent Caption        =   "&IncreaseIndent" End Begin VB.Menu mnuFormatDecreaseIndent Caption        =   "&DecreaseIndent" End End Begin VB.Menu mnuInsert Caption        =   "&Insert" Begin VB.Menu mnuInsertObject Caption        =   "&Object..." End Begin VB.Menu mnuInsertPicture Caption        =   "&Picture..." End Begin VB.Menu mnuInsertbar1 Caption        =   "-" Index          =   2 End Begin VB.Menu mnuPloticusPrefab Caption        =   "Ploticus &Prefab" Begin VB.Menu mnuPloticusScatter Caption        =   "&Scatter Plot" End End Begin VB.Menu mnuInsertbar3 Caption        =   "-" End Begin VB.Menu mnuInsertTextFile Caption        =   "&TextFile..." Shortcut       =   ^T End Begin VB.Menu mnuInsertDate Caption        =   "&Date" Shortcut       =   ^D End Begin VB.Menu mnuInsertbar2 Caption        =   "-" End Begin VB.Menu mnuInsertSymbols Caption        =   "&Symbols" End End Begin VB.Menu mnuData Caption        =   "&Data" Begin VB.Menu mnuEvaluate Caption        =   "&Evaluate" Shortcut       =   {F9} End End Begin VB.Menu mnuTools Caption        =   "&Tools" End Begin VB.Menu mnuWindow Caption        =   "&Window" WindowList     =   -1  'True Begin VB.Menu mnuWindowNewWindow Caption        =   "&New Window" Shortcut       =   {F12} End Begin VB.Menu mnuWindowBar0 Caption        =   "-" End Begin VB.Menu mnuWindowCascade Caption        =   "&Cascade" End Begin VB.Menu mnuWindowTileHorizontal Caption        =   "Tile &Horizontal" End Begin VB.Menu mnuWindowTileVertical Caption        =   "Tile &Vertical" End Begin VB.Menu mnuWindowArrangeIcons Caption        =   "&Arrange Icons" End End Begin VB.Menu mnuHelp Caption        =   "&Help" Begin VB.Menu mnuHelpContents Caption        =   "&HelpContents" Shortcut       =   {F1} End Begin VB.Menu mnuHelpTipoftheDay Caption        =   "&TipoftheDay" End Begin VB.Menu mnuHelpAbout Caption        =   "&About " End Begin VB.Menu mnuHelpSpecialThanks Caption        =   "&SpecialThanks" End End End Attribute VB_Name = "frmDocument" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False

Option Explicit

Here is the code. Some of the routines have very little to do with our principal goals of creating a live mathematical document. This is often the case; as you develop a program you discover small unforeseen problems that need to be solved as you go along in order make the program work properly or simply be comfortable to use.

In VB the Tab key is used to move the focus from on control to the next but when editing text we usually want to actually insert a Tab character. One way to make this happen is to declare an Event Handler for the KeyDown event of the Rich Text Box. This checks to see what the ASCII code of the key is and directly overwrites the selected characters in the Rich Text Box to a Tab character:

Private Sub rtfbox_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = 9 Then rtfBox.SelText = vbTab KeyCode = 0 End If     End Sub

Loading a file is easy. Just call the LoadFile method of the Rich Text Box. The only complication to take care of is that the user could try tpo pen a document that the Rich Text Box can't handle. Here we decide that this is not really an error in the program so we don't raise an error; instead we return a status value: True if we succeeded, false if not.

Public Function LoadFile(rsFile As String) As Boolean On Error Resume Next rtfBox.LoadFile rsFile LoadFile = Err.Number = 0 End Function

Here is the method that actually does the work. Note that all the complicated stuff is in another module. This is because we can easily imagine cases where we would want to automate these things in which case it might be that we get the text from somewhere else

Public Sub EvalDoc goEvalDoc.EvalDoc rtfBox End Sub

Of course there is no point in having a method to recalculate the document if there is no way of running it so we call it from the Data Evaluate menu item. Take a look at the declarations above and see that a short cut key is attached to that menu item (F9):

Public Sub mnuEvaluate_Click EvalDoc End Sub

Remember that the main form's menu is not available when this form is active so we call the File Open event handler of the main form from our own file Open event handler. this is why we had to change from Private to Public (Friend would have worked too):

Public Sub mnuFileOpen_Click fMainform.mnuFileOpen_Click End Sub

To create a brand new document we must call the LoadNewDoc method of the main form:

Public Sub mnuFileNew_Click fMainform.LoadNewDoc End Sub

=Behind the Scenes=

cEvalDoc.cls
This class is where a lot of the hard work is done. The main method, EvalDoc looks very simple because it simply calls three other functions. These functions:

 * pre-process the document so that it is legal JScript,
 * execute the JScript,
 * update the document with the results.

The pre-processing step converts macros into JScript functions that store the location of the text to be replaced in a table and also converts matrices into JScript function calls that return arrays. This makes it practical to assign arrays of values to a variable and deal with the array as a whole instead of each element one at a time.

Here is the header of the class. In the Visual Basic IDE you can't see this text but you can change the values because they are shown in the properties window.

VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0  'vbNone MTSTransactionMode = 0  'NotAnMTSObject END Attribute VB_Name = "cEvalDoc" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit

The actual evaluation of the JScript source code is done by the MSScript control. Not sure what the Attibute is for.

Public moScriptControl As MSScriptControl.ScriptControl Attribute moScriptControl.VB_VarHelpID = -1

The Jscript interpreter only provides the basic functions of JavaScript, any unusual funtions must be provided by us. We do this by creating an object that provides those functions as methods.

Private moFunctions As cFunctions

The document evaluator object must be initialized before use. This means creating an instance of the script control, telling the script control which language will be used and providing a global object that adds extra functions to the interpreter.

Private Sub Class_Initialize Set moScriptControl = New MSScriptControl.ScriptControl With moScriptControl .AllowUI = False .Language = "JScript" .UseSafeSubset = True End With Set moFunctions = New cFunctions moScriptControl.AddObject "Functions", moFunctions, True Set moFunctions.oScriptControl = moScriptControl End Sub

The only public method of this class is EvalDoc which takes a Rich Text box as its only argument, processes the text it finds in it and puts the answers back in the document.

Public Sub EvalDoc(ByRef roDoc As RichTextBox) On Error GoTo ErrorHandler We begin by replacing all the macros we find in the text by JScript function calls that will create the results in the results arrray.

Dim sScript As String sScript = xPreprocess(roDoc.Text)

The results array is a dynamic array that we allow to grow as needed but we don't shrink it because we know that we will evaluate the document again so deallocating the memory would be a waste of time. So we reinitialize the array by simply setting the count of results to zero. The counter is also the pointer to the next free slot in the array.

glResultsCount = 0

Now everything is ready all we have to do is execute the script using the built in Eval function.

moScriptControl.Run "eval", sScript

Lastly, if everything went well, we have to put the answers back into the document.

xUpdateDisplay roDoc Exit Sub

Unfortunately things can go wrong so we must take steps to prevent the program crashing. Problems can occur if the user mis-types a macro by missing out the closing bracket. There can be ordinary syntax errors in the JScript that the user writes and, of course, the program itself can have bugs. So we must have an error handler.

ErrorHandler: Select Case Err.Number

If the user mis-typed the macro by omitting the closing bracket this will be noticed by the preprocessor. In this prototype we handle the problem by selecting the offending text and showing a message box to alert the user.

Case ChunkNotTerminated roDoc.SelStart = xErrData(Err.Description)(0) roDoc.SelLength = xErrData(Err.Description)(1) MsgBox "Missing #> at end of macro"

If the problem is that the macro is correctly formed but unrecognized we do the same as for a syntactically faulty macro.

Case UnrecognizedMacro roDoc.SelStart = xErrData(Err.Description)(0) roDoc.SelLength = xErrData(Err.Description)(1) MsgBox "Unrecognized macro, did you mean to display a value?"

Because we cannot predict what errors will occur we end with a catch all clause that notifies the user. Note the .ErrorContextxxx properties; these are set by the JScript functions that were written by the preprocessor so that the user can be directed at the part of the document that was being processed when the error was discovered.

Case Else With moFunctions .MakeResult Empty, .ErrorContextStart_, _ .ErrorContextEnd_ - .ErrorContextStart_, _ SourceError End With End Select End Sub

Because Visual basic does not have exceptions we need some way of passing information from the routine that raises an error to the routine that catches it. One simple way is to package the information into a string and use the description property of the Err object. Then when the error is trapped we can fetch the data out of the description using the Split function. This little function simply wraps the Split function, partly this is simply to give it a meaningful name but also because it seemed at the beginning that the processing would be more complicated.

Private Function xErrData(ByRef rsErrDescription As String) As Variant xErrData = Split(rsErrDescription, "|") End Function

Before the text of the document can be evaluated by the Script control we must ensure that the text is legal JScript. We do this by taking a copy of the text in a string, looking for directives and macros and generating appropriate code for them.

Output is handled by calling functions that add variable values to the output list. These functions need three arguments: the value to be output, the starting point in the text and the length of the text range that is to be replaced. The user cannot be expected to count lines and maintain these values so a macro is used instead. The output is placed in the text at the point where the macro appears. Unlike most macro replacement systems we won't replace the whole macro with the output because then we would lose the placeholder and would not be able to update it. The macro comprises three parts: intro, body, outro. The intro and outro stay put in the text but the body is replaced by the new output. It is important to choose character sequence that cannot occur as a legal sequence in a JavaScript program and is also unlikely to appear in text string.

The sequence I have chosen is <# #>.

Macros can appear in comments and will work there too.

For now Split and Instr are used to find the markers, regular expressions might be better but I don't know for sure. This function is rather complicated in practice but the basic idea is quite straightforward:
 * split the text into chunks using the intro as a delimiter,
 * note the character offset of each intro string by accumulating the lengths of the chunks
 * remove all text that is in comments,
 * replace the macros with function calls to a function that stores the value of the named variable along with the start and length of the position where it is to be inserted,

All this happens on a copy of the text, the rich text box is not disturbed at this point.

Private Function xPreprocess(rsText As String) As String Const sINTRO As String = "<#" Const sOUTRO As String = "#>" Dim aChunks As Variant

Split the text using the intro sequence. This results in a list (Variant array) of chunks of text that all start with a macro (except for the first item in the list if there was any text before the first macro).

aChunks = Split((rsText), sINTRO) Dim lChunk As Long

The actual text that is executed doesn't need any comments so we create a new text from the chunks by removing the both single line and multiple line comments from the first chunk. This chunk must be dealt with specially because it is the exception to the rule that all chunks begin with a macro.

xPreprocess = xRemoveComments((aChunks(LBound(aChunks)))) Dim lStart As Long

In order for the results to be placed in the text at the correct positions we must keep track of the character offset from the start of the text to the macro. Note that we must always add the length of the intro explicitly because it does not appear in the list of chunks.

lStart = Len(aChunks(LBound(aChunks))) + Len(sINTRO) Dim lEnd As Long Dim lLenChunk As Long Dim lEndVar As Long

Now we can process each chunk and add the processed text to the string to be executed.

For lChunk = LBound(aChunks) + 1 To UBound(aChunks)

First we must check to see if the macro was terminated searching the chunk for the outro. If the outro is missing we raise an error because the user must have made a mistake and forgotten to complete the macro. It might be argued that we should attempt to patch up the text and continue. See Exercises.

lEnd = InStr(aChunks(lChunk), sOUTRO) If lEnd Then Dim sChunk As String sChunk = Left$(aChunks(lChunk), lEnd)

Now we have a complete macro we must check to see if we recognize it. At the moment there is only one type of macro, that is the show value macro. lEndVar = InStr(sChunk, "=") If lEndVar Then xPreprocess = xPreprocess & ";" & vbCrLf _ & "show(" & Left$(aChunks(lChunk), lEndVar - 1) _                     & "," & (lStart + lEndVar) & "," & (lEnd - lEndVar - 1) _                      & ")" & vbCrLf _ & xRemoveComments(Mid$(aChunks(lChunk), _ lEnd + Len(sOUTRO))) lStart = lStart + Len(aChunks(lChunk)) + Len(sINTRO) Else

If the = sign is missing then this isn't a show value macro. As we haven't defined any others this must be an error so we abort the process and report it.

Err.Raise UnrecognizedMacro, "xPreprocess", _ lStart & "|" & Len(aChunks(lChunk)) & "|" & "Unrecognized macro type" End If     Else

If the closing chunk characters are not found we raise an error and abort the process.

Err.Raise ChunkNotTerminated, "xPreprocess", _ lStart & "|" & Len(aChunks(lChunk)) & "|" & "Unterminated chunk" End If   Next lChunk End Function It is simpler to convert the macros to code if we remove the surrounding comments. We can do this on a chunk by chunk basis by defining a function that accepts a chunk and returns the same chunk minus any comments.

Private Function xRemoveComments(ByRef rsChunk As String) As String

Deal with the simplest cases first: single line comments and complete multiline comments.

xRemoveComments = xRemoveBracketed(rsChunk, "//", vbLf) xRemoveComments = xRemoveBracketed(xRemoveComments, "/*", "*/") Now remove any leading or traing multiline comment fragments. These oocur because the chunk split ignores comment boundaries. We search the chunk for the closing and opening multiline comment markers.

Dim lComment As Long lComment = InStr(xRemoveComments, "*/") If lComment Then xRemoveComments = Mid$(xRemoveComments, lComment + Len("*/")) End If   lComment = InStr(xRemoveComments, "/*") If lComment Then xRemoveComments = Left$(xRemoveComments, lComment - 1) End If End Function

This function repeatedly removes substrings that fall between given starting and finishing markers until the starting marker no longer appears in the string.

Private Function xRemoveBracketed(ByRef rsChunk As String, _                                   ByRef rsStart As String, _                                    ByRef rsfinish As String) As String xRemoveBracketed = rsChunk Dim lStart As Long Do     lStart = InStr(xRemoveBracketed, rsStart) ' single line If lStart Then Dim lFinish As Long lFinish = InStr(lStart, xRemoveBracketed, rsfinish)

If the finish marker does not appear then treat the string as though such a marker appeared at the character following the end of the string. This allows us to delete single line comments that do not end in a newline sequence and multiline comments that are split by macros.

If lFinish = 0 Then lFinish = Len(xRemoveBracketed) + 1 End If       xRemoveBracketed = Left$(xRemoveBracketed, lStart - 1) _ & Mid$(xRemoveBracketed, lFinish) End If   Loop Until lStart = 0 End Function

Once the document has been evaluated we must put the answers back in the document at the correct places. This seems at first to be a simple job because we have a list of values together with their locations in the document so at first sight it seems that we need to do is enumerate the results and replace the specified character ranges with the answers. Unfortunately this won't work unless the new text is exactly the same length as the original because inserting new text that is of a different length will move the text that is to be replaced so the results records will point at the wrong place. The solution is to enumerate the results in reverse order so that successive results to are inserted nearer the beginning of the document which means that only text that has already been updated will move.

Private Sub xUpdateDisplay(roDoc As RichTextBox) On Error GoTo ErrorHandler With roDoc

An additional complication is that we would like to preserve the user's selection. We cannot simply store the values .SelStart and .SelLength and later copy them back because replacement may have occurred before or inside the selection, it might even happen that the selection boundaries fall inside a macro. So each time we do a replacement we must update the selection. So the first thing we must do is copy the values of those properties.

Dim lSelStart As Long Dim lSelLen As Long Dim lSelNext As Long lSelStart = .SelStart lSelLen = .SelLength lSelNext = lSelStart + lSelLen

We start at the last result record and count back. The results array is zero based so the results counter always points at the next available slot. Therefore we decrement the counter at the start of the loop, when we finish the counter will be zero.

Do While 0 < glResultsCount glResultsCount = glResultsCount - 1

To replace the text in the rich text box we must first set the .SelStart and .SelLength properties and in order to preserve the user's selection we must compare these values to the current values of the user's selection and update the user's selection if necessary.

.SelStart = gaResults(glResultsCount).Start .SelLength = gaResults(glResultsCount).length If .SelStart + .SelLength < lSelStart Then lSelStart = lSelStart - .SelLength lSelNext = lSelStart + lSelLen End If

Exactly what the replacement text is depends on the result type and on whether or not an error occurred when trying to calculate it. If an error occured then we colour the offending text red and leave it unchanged.

Select Case gaResults(glResultsCount).ResultType Case SourceError .SelColor = vbRed Case Else

If we succeeded then we colour the selection black and replace replaceable part of the macro with the result. Now we see the reason for declaring the result as Variant because at last we see that it is possible to have charts in this document. How the replacement is actually done depends on the type of the result. If the result is a scalar then it is easy to use Visual Basic's string conversion functions to create a human readable representation but a plot is quite a different animal. Because they are so fifferent we create separate functions for them.

.SelColor = vbBlack If TypeOf gaResults(glResultsCount).Value Is cPlot Then xReplacePlot gaResults(glResultsCount).Value, roDoc If .SelStart < lSelStart Then lSelStart = lSelStart + 1 lSelNext = lSelStart + lSelLen End If           Else .SelText = xToString(gaResults(glResultsCount).Value) If .SelStart < lSelStart Then lSelStart = lSelStart _ + Len(xToString(gaResults(glResultsCount).Value)) lSelNext = lSelStart + lSelLen End If           End If        End Select Loop

Now we can set the .SelStart and .SelLength properties again to restore the user's selection. Of course if it included a macro that was replaced then the length of the selection may be quite different from what it was to start with.

.SelStart = lSelStart .SelLength = lSelLen End With Exit Sub

The error handler in this function is incomplete, an exercise for the student perhaps. At the moment is simply asserts a falsehood to stop the program and allow the developer to debug it. In real life this would be quite complicated because we would like to process the whole document.

ErrorHandler: Debug.Assert False Resume End Sub

Because this is really a proof of concenpt rather than a finished aplication we can use a very simply method of converting results to text: just use implicit conversion. The exception to this is matrices. Matrices are JScript arrays so we must do some extra work to format them. Private Function xToString(rvResult As Variant) As String If TypeName(rvResult) = "JScriptTypeInfo" Then ' assume that the object is a JavaScript array xToString = xJArrayToString(rvResult) Else Select Case VarType(rvResult) Case vbDouble, vbLong, vbInteger xToString = " " & rvResult Case vbString xToString = " '" & rvResult & "'" Case Else xToString = rvResult End Select End If End Function

If the result is a plot then the result is really the name of a picture file. To put it in the text we must use the clipboard to insert it, at least that is the simplest way. If the file doesn't exist then we insert text to say so rather than leave the user wndering where the plot is. Note that when this routine is called the selection has already been set to the replaceable part of the macro.

Private Sub xReplacePlot(ByRef rvPlot As Variant, roDoc As RichTextBox) With roDoc If goFSO.FileExists(rvPlot.PicFileName) Then .SelText = ""    ' delete the old plot or whatever else there was. InsertPicture rvPlot.PicFileName, roDoc Else .SelText = "File <" & rvPlot.PicFileName & "> does not exist." End If   End With End Sub

If the result is an array then we format it in rows and columns using tab characters to separate the columns. It is up to the user to set the tabs for that part of the text. Unfortunately we haven't given the user the ability to do this yet, another exercise for the student. Private Function xJArrayToString(rvResult As Variant) As String Debug.Assert TypeName(rvResult) = "JScriptTypeInfo" Dim oRow As Variant Dim lRow As Long Dim vItem As Variant xJArrayToString = vbTab & "[" For lRow = 0 To rvResult.length - 1 If lRow <> 0 Then xJArrayToString = xJArrayToString & vbTab End If     Set oRow = CallByName(rvResult, lRow, VbGet) If TypeName(oRow) = "JScriptTypeInfo" Then xJArrayToString = xJArrayToString & vbTab & xJRowToString(oRow) Else vItem = CallByName(rvResult, lRow, VbGet) xJArrayToString = xJArrayToString & vbTab & "[" & vbTab & vItem & "]" End If     If lRow < rvResult.length - 1 Then xJArrayToString = xJArrayToString & "," & vbCrLf End If   Next lRow xJArrayToString = xJArrayToString & "]" End Function

Each row is in fact a JScript array. JScript doesn't have multidimensional arrays but because everything in JScript is an object we can easily simulate multi-dimensional arrays by having arrays of arrays.

Private Function xJRowToString(rvResult As Variant) As String Debug.Assert TypeName(rvResult) = "JScriptTypeInfo" Dim oRow As Variant Dim lCol As Long Dim vItem As Variant xJRowToString = "[" For lCol = 0 To rvResult.length - 1 vItem = CallByName(rvResult, lCol, VbGet) If VarType(vItem) = vbString Then vItem = "'" & vItem & "'" End If     xJRowToString = xJRowToString & vItem If lCol < rvResult.length - 1 Then xJRowToString = xJRowToString & "," & vbTab End If   Next lCol xJRowToString = xJRowToString & "]" End Function

cFunctions.cls
Predefined functions for EvalDoc. An instance of this class is provided to the JScript object to provide global functions for such things as matrix multiplication. Provides an example of multilanguage programming: the application is written in VB, the document in JScript and the library used by JScript is written in VB.

Option Explicit Public Enum enumFunctionErrors IncompatibleDimensions = vbObjectError + 1 ChunkNotTerminated UnrecognizedMacro End Enum The ErrorContextxxxx attributes are used to enable the errorhandlers to determine the location of the offending source code. Statements are inserted in the code to set these values before the macros.

Public ErrorContextStart_ As Long Public ErrorContextEnd_   As Long Some of the functions need to be able to create variables on the fly so we must provide a reference to the object that is running the script. Public oScriptControl As ScriptControl Plot is a function that the document can call just as though it were a built-in JScript function. It produces an object that in turn is used to drive the ploticus charting program. (Many Thanks to Steve Grubb ). The first argument is a matrix holding the data to be plotted and the second must be a string representing the ploticus command line except that it does not include data=datafilename, that is provided automatically because we use a temporary file. The matrix is written to a file then the a batch file is created to drive ploticus. The ploticus file is executed to create a picture file. The path to this picture file is stored in a cPlot object for later use. Note that Ploticus is very particular about spaces in the command line:

This will fail: 'pl -gif -prefab scat x=1 y =2 data=12 -o 11

because the y =2 should say y=2, note the extra space.

Public Function Plot(ByRef rvData As Variant, ByRef rsPlotCommands As String) As cPlot Set Plot = New cPlot Plot.PicFileName = TF & ".gif" Dim sDataFileName As String sDataFileName = xSaveMatrix(rvData) RunCmd "pl -gif " & rsPlotCommands & " data=" & sDataFileName & " -o " & Plot.PicFileName ' @TODO: check output of plot command for errors End Function

Ploticus reads its data from a file so we must write one. We don't care what it is called and we can delete it afterwards so we create a unique file name. Actually it is possible to create situations where this doesn't work but it is hard. Public Function TF As String TF = goFSO.BuildPath(gsTempFolder, Trim$(NextSerial)) End Function

Because ploticus is a separate program we must start it and wait for it to finish. This routine is a generic command line program executor.

For each parameter that expects an output file name use a macro of the form $(x) where x is the name of a variable that either contains a file name or is to receive an auto-generated file name. The variable can be used later in other commands. The user need not consider what the name actually is if it is used in a sequence of commands because the variable will be given a unique string value on first use that points into the temporary session folder.

If you need to provide a file as input and the data is held in a variable then construct the string by concatenating it with calls to the SaveData function; this writes the variable to a new temporary file and returns the file name.

For example you can drive ploticus like this:
 * s=Cmd('pl -gif -prefab scat x=2 y=3 data=' + SaveData(b) + ' -o $f(plot)')

In this example s receives the output from standard out (if any) and a variable named plot names the file that will receive the ploticus picture. If the plot variable is an empty string then a unique temporary file name will be created for it.

If you want to re-use a file name make sure that you clear it first unless you really want to use the same name again. Macros are $x(args) where x is the name of the macro and args is whatever arguments the macro takes in whatever form in takes them.

Public Function Cmd(ByRef rsCommandLine As String) As cPlot RunCmd rsCommandLine ' ' @TODO: check output of plot command for errors End Function

Ploticus needs the data for the plot in a file with a particular format. The routine that starts ploticus needs to know the name of that file. This function creates the file, writes the data to it and returns the file name.

Private Function xSaveMatrix(rvData As Variant) As String xSaveMatrix = goFSO.BuildPath(gsTempFolder, Trim$(NextSerial)) & ".dat" goFSO.OpenTextFile(xSaveMatrix, ForWriting, True).Write _ JArrayToPlotData(rvData) End Function The macros in the document create result records in the gaResults array by calling this function.

Public Sub MakeResult(ByRef rvResult As Variant, _                       ByRef rlStart As Long, ByRef rlLength As Long, _                        ByRef reResultType As eResultType) With gaResults(glResultsCount)

Note that we can't use TypeOf to find out if the result is a JScript object because the type doesn't exist according to VB. This is very strange because the Locals and Watch windows show it correctly so the IDE must know. Use typeName instead, the disadvantage is that string comparisons are slower.

If TypeName(rvResult) = "JScriptTypeInfo" Or IsObject(rvResult) Then Set .Value = rvResult Else .Value = rvResult End If     .Start = rlStart .length = rlLength .ResultType = reResultType End With

Another exercise for the student: what happens if glresultsCount is greater than the upper bound of the array? What should be done here?

glResultsCount = glResultsCount + 1 End Sub

The Show function is public so that JScript can call it. The macro replacement process puts these calls in the source code replacing the macros. At the moment this simply calls the Makeresult routine. the reason for the wrapper is to facilitate more sophisticated error handling.

Public Function Show(o As Variant, rlStart As Long, rlLength As Long) MakeResult o, rlStart, rlLength, Result End Function

Multiply to JScript matrices together and return another JScript matrix. This is just a wrapper for the low level matrix multiplication routine. This wrapper converts the incoming matrices from JScript to VB and the result from VB to JScript.

Public Function Multiply(ra1 As Variant, ra2 As Variant) As Object On Error GoTo ErrorHandler Set Multiply = VBMatrixToJArray(xMultiply(JArrayToMatrix(ra1), JArrayToMatrix(ra2))) Exit Function ErrorHandler: MakeResult Empty, ErrorContextStart_, ErrorContextEnd_, SourceError End Function

This function multiplies two matrices. It is called from the public function Multiply.

The rules for multiplying matrices are given in Matrix Operations in the Algebra book. This function is a straightforward implementation using essentially the same notation as shown on that page. The one notable difference is that our array indices extend from zero upwards instead of from one upwards.

Public Function xMultiply(ByRef raA As Double, ByRef raB As Double) As Double Dim j As Long Dim k As Long Dim m As Long Dim n As Long Dim p As Long Dim i As Long Dim aC As Double Dim cij As Double

Remember that the Ubound function accepts an optional second argument that tells which dimension to return; the first dimension is number one, the second number two and so on. Matrices have only two dimensions, the first is the row, the second is the column.

n = UBound(raA, 2) m = UBound(raA, 1) p = UBound(raB, 2) ReDim aC(0 To n, 0 To p)   For i = 0 To m      For j = 0 To p        nAcc = 0 For k = 0 To n         cij = cij + raA(i, k) * raB(k, j)        Next k        aC(i, j) = cij Next j   Next i    xMultiply = aC    Exit Function End Function

Exercises

 * The strategy of reading the results list from the end instead of the beginning is not guaranteed to work for all documents. Can you explain why?  Hint, think of the different kinds of JScript statements that you could use.

modJavaScript
This module is for functions that help connect the JavaScript document to the Visual Basic world inside the program.

This implementation provides some simple matrix manipulation functions. Because these are written in Visual Basic and the matrices must be written in JScript we need functions to convert between JScript objects and Visual Basic arrays.

Option Explicit

JScript matrices are actually nested arrays. Each row is a one dimensional array of elements and the matrix is also a one dimensional array where each element is an array. This means that JScript matrices can be ragged arrays. In Visual Basic it is usual to represent a matrix as a rectangular array. In this implementation we shall simply assume that all rows in the array have the same number of elements so we can discover the number of rows by checking the count of the outer JScript array and the number of columns by checking the count of elements in the first row.

This function converts a JScript array to a Visual Basic array.

Public Function JArrayToMatrix(rvResult As Variant) As Double Dim oRow As Variant Dim lRows As Long Dim lCols As Long

Finding the number of rows is easy because all JScript objects are in fact dictionaries and they all have a length property. Getting the number of rows is slightly more complicated. We must first get hold of the JScript object that is the first row. Remember that JScript objects are actually dictionaries so arrays are dictionaries where the keys are numbers. In the Script control this is mapped so that each item appears to be a property of the object so we can use CallByName to retrieve the value. The name in this case is simply 0 (that's the numeral zero) because JScript arrays are numbered from zero to length - 1.

lRows = rvResult.length Set oRow = CallByName(rvResult, 0, VbGet) lCols = oRow.length

Now we can allocate the Visual Basic array. To avoid confusion we explicitly specify both the lower and upper bounds; this is good practice because it means that you don't have to wonder whether or not there is an Option Base statement at the top of the file.

ReDim JArrayToMatrix(0 To lRows - 1, 0 To lCols - 1)

Now we simply enumerate the rows and copy the contents one row at a time into the array.

Dim lRow As Long Dim vItem As Variant For lRow = 0 To lRows - 1 Set oRow = CallByName(rvResult, lRow, VbGet) xJRowToMatrix JArrayToMatrix, lRow, oRow Next lRow End Function

In the interests of readability the copying of data from a row is performed in a separate routine. Although, in this case, it doesn't add much readability it does serve to clearly distinguish the row operations from the column operations. Note that this is a subroutine that accepts a reference to the target array and a row number because we cannot assign to a row in Visual Basic.

Private Sub xJRowToMatrix(raMatrix As Double, _                           rlRow As Long, _                            rvResult As Variant) Dim lCol As Long Dim vItem As Variant For lCol = 0 To rvResult.length - 1 vItem = CallByName(rvResult, lCol, VbGet) raMatrix(rlRow, lCol) = vItem Next lCol End Sub

Converting from a Visual Basic array to a JScript matrix can be done by creating a snippet of JScript source code and evaluating it. All we have to do is create a string that looks just as the user would type it. Presumably direct manipulation of a JScript object would be faster, if anyone can find out how.

Note the late binding of the function result. This is because the objects exposed by the Script Control seem to not implement the interfaces that Visual Basic needs. This could be because of the extra layer of indirection provided by the Script Control.

Public Function VBMatrixToJArray(raMatrix As Double) As Object Dim lRow As Long Dim lCol As Long Dim sArray As String sArray = "[" For lRow = LBound(raMatrix, 1) To UBound(raMatrix, 1) sArray = sArray & "[" For lCol = LBound(raMatrix, 2) To UBound(raMatrix, 2) sArray = sArray & raMatrix(lRow, lCol) If lCol < UBound(raMatrix, 2) Then sArray = sArray & "," End If     Next lCol sArray = sArray & "]" If lRow < UBound(raMatrix, 1) Then sArray = sArray & "," End If   Next lRow sArray = sArray & "]" Set VBMatrixToJArray = goEvalDoc.moScriptControl.Eval(sArray) End Function

cPlot.cls
Represents a plot. Allows the tostring function to decide what to do to display the plot. The tostring function will use the filename property to find the picture file created by Ploticus and embed that into the rich text file.

Option Explicit Public PicFileName As String

modPlot.bas
This module provides the low level connections to ploticus.

Option Explicit

There are at least two ways to put a picture into a rich text box. The one chosen here requires the least programming but has the disadvantage that it uses the Windows clipboard which is rather impolite because the user might have something in it at the time. To do this we use the SendMessage API call which sends windows messages to window handles.

Private Const WM_PASTE = &H302& Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hwnd As Long, ByVal wMsg As Long, _          ByVal wParam As Long, lParam As Any) As Long

Insert the picture that was created by ploticus by loading it into the clipboard and then instructing the rich text box to paste it in.

Public Sub InsertPicture(rsFile As String, rtfText As RichTextBox) Clipboard.Clear Clipboard.SetData LoadPicture(rsFile) SendMessage rtfText.hwnd, WM_PASTE, 0, 0& End Sub

Ploticus needs a file of data in rows and columns. This data is represented in our document as a matrix. We must convert that matrix to a string and then save it to a file. This function does the string conversion part.

Public Function JArrayToPlotData(rvResult As Variant) As String Debug.Assert TypeName(rvResult) = "JScriptTypeInfo" Dim oRow As Variant Dim lRow As Long For lRow = 0 To rvResult.length - 1 Set oRow = CallByName(rvResult, lRow, VbGet) If TypeName(oRow) = "JScriptTypeInfo" Then JArrayToPlotData = JArrayToPlotData & xJRowToPlotData(oRow) Else JArrayToPlotData = JArrayToPlotData & " " & CallByName(rvResult, lRow, VbGet) End If     If lRow < rvResult.length - 1 Then JArrayToPlotData = JArrayToPlotData & vbCrLf End If   Next lRow JArrayToPlotData = JArrayToPlotData End Function

Private Function xJRowToPlotData(rvResult As Variant) As String Dim oRow As Variant Dim lCol As Long For lCol = 0 To rvResult.length - 1 xJRowToPlotData = xJRowToPlotData & CallByName(rvResult, lCol, VbGet) If lCol < rvResult.length - 1 Then xJRowToPlotData = xJRowToPlotData & " " End If   Next lCol End Function

Exercises
The JArrayToPlotData is very similar to the function that converts a JScript matrix to a VB array. rewrite JArrayToPlotData so that it uses that function instead of doing that conversion from JScript on its own. Does this improve the program? Hint: has the amount of code to be maintained been reduced or has duplication (or near duplication) of code been eliminated?

modShellWait
JArithmetic uses external programs to do some of the more complicated work. These programs are often command line programs so we need some wrapper functions to make the rest of the program think that they are built in functions.

The original of this code was found on http://www.msfn.org/board/lofiversion/index.php/t35615.html.

Option Explicit Private Const SYNCHRONIZE = &H100000 Public Const WAIT_OBJECT_0 = &H0 Private Declare Function OpenProcess Lib "Kernel32.dll" _ (ByVal dwDA As Long, ByVal bIH As Integer, _          ByVal dwPID As Long) As Long Private Declare Function WaitForSingleObject Lib "kernel32" _ (ByVal hHandle As Long, _          ByVal dwMilliseconds As Long) As Long This function is the one used to execute an external program. It waits no more than ten seconds (10000 milliseconds). Unfortunately it does not know how to deal with failures. This is left as an exercise for the student.

Public Sub RunCmd(CmdPath As String) On Error GoTo Err_RunCmd If ShellWait(CmdPath, 10000) Then Stop ' error ' @TODO: do something useful here End If   Exit Sub Err_RunCmd: Stop ' do something useful here End Sub This is the lower level function that executes the command line and waits for a time specified in the call. See External Processes.

Function ShellWait(CommandLine As String, _                    TimeOut As Long) As Boolean Dim ProcessID As Long Dim hProcess As Long

The Shell command returns a ProcessID which is not actually very useful because almost all Windows API functions use the Process Handle, this isn't a problem though because the OpenProcess API function can translate between the two.

ProcessID = Shell(CommandLine)

If the Process ID is non-zero then the process was created and started so we can just wait for it to complete. To wait we use an API function called WaitForSingleObject that takes the Process Handle and a timeout in milliseconds. This function simply waits until the process terminates or the timeout expires; it returns a status code to say which.

If ProcessID Then

Non-zero (True) so Shell worked. Now get a process handle for the PID (Wait takes a handle).

hProcess = OpenProcess(SYNCHRONIZE, False, ProcessID) If hProcess Then Dim lResult As Long lResult = WaitForSingleObject(hProcess, TimeOut) If lResult = WAIT_OBJECT_0 Then ShellWait = True Else ShellWait = False End If     Else

Failed to get process handle. This can happen if the process terminated very quickly or it might not really have executed at all even though Windows started a process. Return false to the caller to say it failed.

ShellWait = False End If   Else

If the Process ID is zero then the Shell failed.

ShellWait = False End If End Function

=Start up and project file=

modMain.bas
The main module is the one that contains all the code that starts the application and gets it into its initial state. In this case we also use it as a place to declare various constants and global functions.

Attribute VB_Name = "modMain" Option Explicit

The constants gsFILE_FILTER and gsSAVE_AS_FILTER are used when we show the Common Dialog boxes to open and save files. The tell the dialog which file masks to put in the combo box. Notice that we have provided for rtf, txt, all files. This means that the user can open files that are plain text and save them as rtf. Of course the user can also attempt to open a file that isn't either text or rich text, we must cope with that. The declarations are public because they are actually used in a different code module.

Public Const gsFILE_FILTER As String = "Rich text Format(*.rtf)|*.rtf|Text (*.txt)|*.txt|All Files (*.*)|*.*" Public Const gsSAVE_AS_FILTER As String = "Rich Text Format(*.rtf)|*.rtf|Text (*.txt)"

The hard work is done by an instance of the cEvalDoc class'. We never need more than one instance of this class so we declare it here as a global variable and instantiate it in the main subroutine.

Public goEvalDoc As cEvalDoc

The macro processor needs to keep track of where in the text replacements are to occur and what the replacement actually is. The tResult user defined type (UDT) is used to hold the necessary information. Note that the Value member is declared as Variant because the result of an expression can be anything not just a number.

Public Type tResult Value As Variant Start As Long length As Long ResultType As eResultType End Type The result type is an enumerated type. This makes the code easier to read. Notice that none of the members has been explicitly assigned a value, this is because we don't care what the values are so long as they are distinct.

Public Enum eResultType SourceError SourceNoError Result End Enum

We need a place to store the values that we will substitute back in the text because we can't put them in untill we have finished processing the whole document. The reason for this is that the length of the result might be different from the length of the text it replaces. We maintain a count of the results so that we can let the gaResults array expand. When we evaluate the document again we start off by simply resetting glresultsCount to zero; this saves time by not requiring the results array to be eallocated every time.

Public gaResults As tResult Public glResultsCount As Long

The FileSystemObject is much simpler to use than the older built in VB functions for reading files. To use it you must have the Scripting Runtime Library installed.

Public goFSO As FileSystemObject

Some of the functions used in this program need to create temporary files. So that we are sure to avoid collisions between different instances of this program and with other programs we create a new temporary folder inside the system temporary folder and store its name in this variable.

Public gsTempFolder As String

The main routine initialise the application. It creates the temporary work area, creates an instance of the document evaluator, shows the main form and allocates the results array.

Sub Main Set goFSO = New FileSystemObject On Error GoTo ErrorHandler ' Create temporary area for this instance of JArithmetic. gsTempFolder = Environ$("TEMP") & "\" & App.EXEName If Not goFSO.FolderExists(gsTempFolder) Then goFSO.CreateFolder gsTempFolder End If   gsTempFolder = gsTempFolder & "\" & Format$(Now, "yymmddhhmmss") If Not goFSO.FolderExists(gsTempFolder) Then goFSO.CreateFolder gsTempFolder End If   Set goEvalDoc = New cEvalDoc Load fMainform fMainform.Show ReDim gaResults(0 To 100) Exit Sub ErrorHandler: Debug.Assert False MsgBox Err.Number & ", " & Err.Description & ". Command line = <" & Command$ & ">", vbOKOnly, "Arithmetic" Resume End Sub

The application can be closed in several different ways but each one should end with a call to this function so that all the documents are correctly closed. So far there is no implementation of automatic saving, nor is the user prompted to save changed documents. This is the place to add such code.

Public Sub ExitApplication Dim oForm As Form For Each oForm In Forms Unload oForm Next oForm End Sub This routine creates a unique number. It is used by functions that need to create temporary files. The result is always a whole number but we use Double instead of Long so that we 'never' run out of numbers (in this aplication this refinement is hardly necessary).

Public Function NextSerial As Double Static nSerial As Double nSerial = nSerial + 1 NextSerial = nSerial End Function

prjJarithmetic.vbp
Here is the Visual Basic project file (VBP) used to tie all this together. In principle it should be possible to automate the downloading and compilation of this. Do bear in mind that any absolute paths you find in this VBP might not point to anything when copied to your computer.

Type=Exe Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\..\WINNT\System32\stdole2.tlb#OLE Automation Reference=*\G{420B2830-E718-11CF-893D-00A0C9054228}#1.0#0#..\..\..\..\..\..\..\WINNT\System32\scrrun.dll#Microsoft Scripting Runtime Reference=*\G{0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}#1.0#0#..\..\..\..\..\..\..\ProgramFiles\Hikari\msscript.ocx#Microsoft Script Control 1.0 Object={F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0; ComDlg32.OCX Object={831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0; MSCOMCTL.OCX Object={38911DA0-E448-11D0-84A3-00DD01104159}#1.1#0; COMCT332.OCX Object={3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0; RICHTX32.OCX Class=cEvalDoc; cEvalDoc.cls Form=Fmainform.frm Form=frmDocument.frm Class=cFunctions; cFunctions.cls Module=modMain; modMain.bas Class=cPlot; cPlot.cls Module=modPlot; modPlot.bas Module=modShellWait; modShellWait.bas Module=modJavaScript; modJavaScript.bas RelatedDoc=..\doc\jarithmetic.htm RelatedDoc=..\debug\index.html Module=modGPL; ..\..\common\gpl\modGPL.bas IconForm="fMainform" Startup="Sub Main" HelpFile="" Title="prjArithmetic" ExeName32="Arithmetic.exe" Path32="debug" Name="prjJArithmetic" HelpContextID="0" Description="Arithmetic document processor" CompatibleMode="0" MajorVer=1 MinorVer=0 RevisionVer=2 AutoIncrementVer=1 ServerSupportFiles=0 VersionCompanyName="Kevin Whitefoot" VersionFileDescription="Embedded JScript document processor." VersionLegalCopyright="Copyright Kevin Whitefoot, 2005" VersionProductName="JArithmetic" CompilationType=0 OptimizationType=0 FavorPentiumPro(tm)=0 CodeViewDebugInfo=0 NoAliasing=0 BoundsCheck=0 OverflowCheck=0 FlPointCheck=0 FDIVCheck=0 UnroundedFP=0 StartMode=0 Unattended=0 Retained=0 ThreadPerObject=0 MaxNumberOfThreads=1 DebugStartupOption=0


text:
User talk:Fsateler

Thanks for the addition to the arclength page on wikibooks calculus. I had not thought of putting that in. Juliusross 11:14, 13 October 2005 (UTC)

Sorry to be fussy, but you should say that you are assuming that f and g have continuous first derivatives.

Also the last line "This is equivalent to:" the integral is correct. But it is not quite the definition of the Riemann integral as it is usually given (as you have c_i and d_i instead of just c_i). It works because \sqrt{a^2+b2} is continuous function of two variables. I am not sure if it is worth mentioning it as it is a technicality.

If you want me to I can think if I can find a way around this problem Juliusross 18:14, 22 October 2005 (UTC)

blanking
--Derbeth talk 13:27, 4 December 2005 (UTC)

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

title: User talk:Fsateler


text:
Apache Ant/Fileset

FileSets are ant's way of creating groups of files to do work on. These files can be found in a directory tree starting in a base directory and are matched by patterns taken from a number of PatternSets and Selectors.

FileSet identifies the base directory tree with its dir attribute. Then the FileSet's enclosed pattern elements, both named (PatternSets) and selected by wildcards (Selectors), choose the files and folders within the base tree.

If any selector within the FileSet do not select a given file, that file is not considered part of the FileSet. This makes FileSets equivalent to an selector container.

Wildcards
Wildcards are used by ant to specify groups of files that have a pattern to their names.
 * ? : is used to match any character.
 * * : is used to match zero or more characters.
 * ** : is used to match zero or more directories.

Examples
The below FileSets all select the files in directory ${server.src} that are Java source files without "Test" in their name.

Finally
FileSets can appear as children of the project element or inside tasks that support this feature.

Next Section, Next Cookbook Chapter

title: Apache Ant/Fileset


text:
Talk:Introduction to Philosophy/Transcendentalist Theology Transcendentalism

One important question for transcendalists (historically, anyway) is that of natural disasters, such as earthquakes. Can we equate natural disaster with evil caused by (a) god? Insurance claims still refer to natural disasters as acts of god. Answering this question forces a choice about omnipotence and omnibenevolence. It would be nice to see some discussion of this in the article. 64.150.65.50 (discuss) 18:07, 16 February 2012 (UTC)

yabba ditty ditty doob ............interesting nice!


text:
Budget Watch Collecting/How to buy and sell watches

Budget Watch Collecting/Buying on Ebay

Budget Watch Collecting/Selecting a watch

Budget Watch Collecting/Picking Watches for Resale

Budget Watch Collecting/Selling a Watch on the Internet


text:
Budget Watch Collecting/Buying on Ebay

Feedback: Ebay's feedback system is fairly easy to manipulate. Unscrupulous sellers can wait until they get feedback from a buyer before they leave theirs--If a buyer leaves a negative, the seller can also leave a negative, then ask for mutual feedback removal.

99% is suitable feedback, anything below 98% should be investigated by checking the feedback comments, and looking for neutrals and mutually-removed feedback. Check for attitude, and check whoever gave the feedback. Toolhause.org has a great set of tools to find just the negative and neutral feedback. Update--As of 2008, Ebay has changed how feedback is calculated, and removed the ability of sellers to leave negative for buyers. It appears that this is resulting in overall lower feedback scores for buyers.

Negative signs--These aren't _all_ reasons to avoid a deal, but if there are too many of them be cautious.

Low number of transactions, but a non-standard, professional looking page layout.

"Easily repaired"--Although this may be technically true, almost all repairs will involve servicing--Disassemble, clean, reassemble, lube and adjust, plus any replacement parts. $40 is cheap for a proper servicing. "Easily repaired" from a professional watch seller--if it is easy, why not get the repair done and sell it for more?

"Overwound"--Apparently this is intended to mean that someone wound the watch too much and the repair is simple. There's no such thing as overwound--instead there is something that has kept the watch from unwinding. This could be simple dirt, requiring a basic servicing, or damaged parts that have to be hand-made at great expense.

"Just needs a battery (no returns)". Likely true if it is an inexpensive quartz, otherwise be careful. Especially true for Accutrons. "Works with test battery" or "will include new battery for $" is probably OK. 'quits when face up/face down'--Probably a broken balance pivot or jewel.

Blurry or tiny pictures: Potluck. Watches are difficult to accurately photograph. If it is an occasional seller, or someone who specializes in a different type of item, it may be OK. If they sell lots of watches, possibly hiding something. I've had some very good luck buying items with bad pictures from people who don't sell watches regularly.

Pay attention to shipping charges, and avoid sellers with excessive charges. If no shipping is listed, always ask costs before bidding.

Positive signs:

Picture of the movement, especially for vintage mechanicals. Not so important for Timex or quartz.

Many clear pictures

Pointing out flaws

Returns accepted

High feedback, both percentage and number of transactions.

Clear statement about running condition--"Runs and keeps time", "Keeps time to within XX per day" "Doesn't run, balance good" "Runs and stops" (Assuming you can deal with whatever problems they say)

Watch lots:

It is fairly frequent that a batch of watches will sell for about the price of the best watch in the batch. I will typically add up the minimum I think the watches will re-sell for individually, and bid accordingly.

title: Budget Watch Collecting/Buying on Ebay


text:
Celestia/Suggested New Features

This page is obsolete. See the page Bugs and Feature Requests.

Features requested on this page are very unlikely to be implemented if they aren't on the | SourceForge list. If you are interested in any of the features listed below, you must take the responsibility of making sure they're requested on | SourceForge.

= Keyboard Commands =

Customized Command Key Assignments
It might be useful if it were possible to specify customized command key assignments in a configuration file.

Scripted Key Commands
It might be useful if it were possible to create user-defined key commands using Lua scripting.

 * This already is possible. See the example script elsewhere in this WikiBook. ...Selden 19:43, 2 September 2007 (UTC)

= Time Controls =

Change Time Flow Rate While Paused
It might be useful if it were possible to change the time flow rate while paused, so that when the pause is ended time would flow at the new rate.

 * This is possible in Celestia v1.4.1 ...Selden 19:44, 2 September 2007 (UTC)

GOTO Spacecraft In Time
Currently, one needs to look at the SSC file to see when a spacecraft begins (such as Mir). Doctorjoe created a simple patch, which I have applied to my OS X builds without a problem, that shows the start and end times of a spacecraft in the view window. In addition, hitting shift-G, takes you to the spacecraft's temporal and physical location.

I have this patch now... and would be happy to send it to anyone who wants to try incorporating it into CVS (contact: BlindedByTheLight).

Furthermore, it might be cool to allow more than just two dates (start and end) to appear in the view. Various spacecrafts have SEVERAL temporal points of interest. Perhaps changing the code so that people could declare various times with text labels...

Start Time: 34747373.4234234

Start Text: "Ship takes off"

Date1: 35343434.2343

Date1Text: "Ship passes the moon"

Date2: 35324344.2343223

Date2Text: "Ship circles Jupiter"

End Time: 342423434

End Text: "Ship crashes into Io"

Then a keyboard shortcut to go to each successive point in time...? Would be cool and would open up a whole new world for add-ons designers.

= Render Options =

Improved illumination phase functions
It might be nice to allow planet/satellite illumination functions to vary dependent on the surface type. Presently objects like Jupiter are more realistic with the default lambert function. Solid surfaces are less realistic and might be improved with a type of "lunar lambert" function.

Improved distance filters
It might be nice to allow distance filters for any object, not only stars. When you have a populated system, it's annoying see all/none names/orbits. It could be possible show only largest/important/brightest names/orbits of objects from your FOV.

Display coordinates
It might be useful to show galactic and equatorial coordinates.

Display apparent magnitude for planetary bodies
It would be interesting to have the apparent magnitude of the selected planetary body displayed. The program already performs these calculations, for instance, to set the scale of the planetary scaled discs, so I assume it wouldn't be too difficult to add this information to the planetary information display.

Display axis
It might be useful to show rotation axis of bodies which I selected to see their orbits.

Display speed/forces
It might be useful to show ship's speed/forces (angular and radial) when I do a follow/sync orbit/track selection.

Create visual filters
It might be wonderful to apply filters: select between infrared, visible, ultraviolet, X-ray and gamma spectrum, simple polarized colors, aberration of light, high level of ambiental light. A stereographic mode, for use with red-cyan glasses, could be excellent (StarStrider have this).

Improved real-time clouds
It might be nice to update clouds from public databases.

= View Controls =

Zoom to Fill
It might be useful if there were a key command to cause the view to zoom so that the selected object fills the screen. Then the user could select an object of small apparent size (by onscreen picking or keying-in its name), press a key to center it in the view, and press another key to zoom the view so the object fills the screen. This would be more convenient than manual zooming.

Select Orbits
It might be useful if you can select which orbits (from one group) you will see (if orbits turned on) - for example only from objects with a special size.

Especially if you have many asteroids in you Solar System Browser it becomes very complex in the presentation. An alternativ or additionally it might be useful to group orbits close to each other in a "fuzzy" orbit - similary to the rings of a planet.

Third-Person View
It might be useful if you can see from perspective an Official Celestial Ship for navigation.

= Solar System Browser =

Presentation
It might be useful if you can categorise and / or group planets and asteroids, so that a list doesn't become too complex.

Proximity lists
It would be interesting to have a list of the closest objects to the current position not only in the star browser, but also in the planetary one. It would be even more interesting if the program included a function that returned the closest approaches to a given body (or between two bodies of choice) in a given interval.

= Orbits =

Galactic Orbits (stellar proper motion)
It might be useful if galactic orbits could be simulated based on stellar proper motion where available.

Segmented Orbits
It might be useful if orbits could be defined with multiple segments, combining elliptical orbit segments with different elements, xyz files, etc. Also if orbits for objects could be made up of different ephemeris data blocks. i.e. so the orbit is correct for various dates of interest. This would be useful when considering planet/moon events and spacecraft missions etc. At the moment using beginning and ending nearly works but celestia then has multiple objects with the same name and does not switch to the visible object of the same name at the specified ending of the objects life. Hence the satellite/Moon/asteroid disappears and the new object pops up but is not tracked only the "ghost" object.

Horseshoe and Tadpole Orbits
It might be useful if horseshoe and tadpole orbits could be specified.

SPICE Orbits
It might be useful if orbits could be specified using NASA's SPICE files.

 * SPICE orbits will be supported in Celestia v1.5.0. They already can be used in its prereleases. See the documentation elsewhere in this WikiBook. ...Selden 19:49, 2 September 2007 (UTC)

Horizons Orbit Data Extraction Tool
It might be useful if there were a tool for extracting orbit data from JPL's Horizons system into a Celestia-compatible format.

Critical Interval Orbit Overrides
It might be useful if there were a way to specify xyz data for higher-precision during critical time intervals (e.g. spacecraft flybys) while using less precise orbital data at other times.

Multiple Star Orbit Display
It might be useful if orbits could be displayed for binary/multiple stars.

 * This already is possible in Celestia v1.4.1. See the documentation elsewhere in this WikiBook. ...Selden 19:51, 2 September 2007 (UTC)

Detect collisions
It might be useful if I could select 2 bodies and calculate past and future collisions as Eclipse Finder. Or merge both as an "Events Finder" with alerts.

Extended eclipse finder
It would be real neat to have an extended eclipse finder, able to find them for any body, not just the predefined ones.

= Browser =

Show Stars and Planets around Barycenters
It might be useful if it would be possible in the Star Browser to select the barycenter of a binary and then see the corresponding stars with subsequent planets or planets around the barycenter.

Improved search
It might be useful if you can search a name and see all objects which matches it (in any part of the word). And could be interesting show all known names from an object (for example, 2003 UB313 / Xena)

= Label Display =

Star Label Density Control
It might be useful if it were possible for the user to control the density of star labels, e.g. with a magnitude limit.

Catalog Preference for Star Labels
It might be useful if it were possible for the user to specify a precedence ordering of catalogs for selecting which star labels to use. = Data Loading =

Reload Data Without Restart
It might be useful if it were possible to reload the data without restarting the program. This would make it more convenient to test incremental changes to data. Such a reload should be able to reload one file, one directory or all of the data.

= Scripting =

Disable User Inputs During Script
It might be useful if there were a script command to disable user inputs while the script is running. Another script command could reenable inputs if desired. The inputs wouild be reenabled automatically when the script ends, and of course the "escape" key to abort the script would always be enabled.

Write Messages to Log from Script
It might be useful if were possible to write messages to the log from a script.

Display Error Messages in Log
It might be useful if scripting error messages were displayed in the log.

= Views =

Save Multiple Views in Celestia URLs
It might be useful if multiple views could be saved in a Celestia URL. This would make it possible to easily restore a desired view layout.

Separate Rendering Flag Settings for Each View
It might be useful if Celestia allowed separate rendering flag settings for separate views. This would make it possible, for example, to show orbits in one view but not another.

Default Bookmarks
It might be useful to create default views such as solar system view from above.

= Data Files =

Merge STC and SSC file formats
At present Celestia requires separate file formats for stars (STC) and other bodies (SSC). It would work better if these were combined into a single file format with features of both.

Planetary Barycenters
The modelling of planets with large moons is unrealistic because the barycenter of the planet-moon system is always the center of the planet. If a barycenter for a planet-moon system could be specified, the motion of the planet and moon can be modelled more accurately. Examples of systems that can be modelled correctly with this new feature would be Pluto-Charon, Earth-Moon, and certain binary asteroids.

 * I believe you can currently create barycenters for planets/moon/asteroids, just not in .ssc files. They have to go in .stc files. Granted, this is an inconvenience.

 * The Class "invisible" hack was defined long ago for just this purpose. Celestia v1.5.0 adds the ReferencePoint directive for use in SSC files. ...Selden 19:56, 2 September 2007 (UTC)

Local InfoUrls
Local InfoUrls: have Celestia check local directories for InfoUrl files (html/cel/celx) before trying to look it up on the www. Alternatively, allow local InfoURL files to be specified explicitly.

 * This latter capability already exists. ...Selden 19:58, 2 September 2007 (UTC)

Spectral Light Emission
Implement a method to change the spectral color of stars in the STC. If for example you want to create a green star you can now add a green star texture with the "texture" command, but the halo of the star still has the color of the originally selected type i.e. "G2V". So the star doesn't look right. Perhaps a command with [R G B] values for this could be added.
 * See: "Why aren't there any green stars?" Briefly, stars are black body radiators, so the spectral emission of a star is determined by its temperature. Stars emit light at essentially all wavelengths across the spectrum. We perceive color based on the wavelengths to which the receptor cells in our eyes are sensitive. Black body spectra don't have the proper proportion of radiation at different wavelengths to cause our eyes to perceive the light as green.

Multiple Names in the SSC
It might be useful if the objects in the SSC allowed multiple names like stars. This would make it possible, for example, to select asteroids by not only its name but also the number. And this allows enabling translation of the objects' name into various languages keeping original English name.

Customized body categories
(I'm not sure where this one fits best. Feel free to move around) It would be useful to be able to set up customized object categories, for instance, to separate main body asteroids from TNOs or dwarf planets, or whatever the user sees fit.

title: Celestia/Suggested New Features


text:
Talk:XML - Managing Data Exchange/Recursive relationships

 * Isn't the Many to Many recursive diagram incorrect? Surely one set of crows feet should be on the package and another on the activity. - Peter Johnson - peter.johnson@screechlaboratories.co.uk

 * Use a similar example throughout the chapter for the 1:1, 1:M, and M:M sections. Perhaps one that relates to business.

 * Bold key words and start with clear definition after each.

 * Follow author guidelines to revise chapter.

--- XML: Managing Data Exchange/Recursive relationships - Editorial Board revisions - Viji

 * remove the XSL items from the Learning Objectives

Introduction

"... such as a one-to-one, one-to-many, and many-to-many" - those are the only relationships that we have studied so far, therefore using "such as" does not really make sense.

 * Where (and if) appropriate change the headings that read "one to one model" to "one to one recursive model" etc.

a look at new elements - change to New schema elements

 * bold the element tags just to highlight them and set them apart

--- that same sentence should probably read "only one" instead of "one only"
 * also follow the tag with its definition then set a break before the rest of the explanation / examples
 * what does "On {32}... " mean? Does it mean line 32? Since there are no line numbers and we are not using line numbers because they really don't match up, find another way of referencing the code line (if infact that is what is going on here).  you can even copy and paste the code refered to here
 * "Experiment with changing the predecessor value on of the XML file above to “1”, and you will see that the file will not validate!" - include where in the XML document, which variable must be changed to "1".
 * xpath is introduced for the first time here, please give a brief definition or description - it does not have to be exacting, it can be general - also do this before the  element, mentioning that it will come later in the paragraph

 change examples to fit with the tourguide model

(one to one recursive: perhaps use changes in the names of cities (like Istanbul was Constantinopal) but you would need at least three names; or from the previous 1:1 chapter example you could say that the top destination changes every year and make a recursive relationship from that)

(one to many recursive: perhaps use a state and cities and a city that is the state capital - ignore the bit about some countries somewhere with two capitals - you can make it smaller, perhaps 2 states with 4 cities one of which is the capital)

(many to many recursive: perhaps use a tourist package with air flights, hotel nights, museum trips, sightseeing, etc. which can be bought separately or in various packages)

 * once again I think line numbers are being used ({41} and {18 - 21}), refer to the code in another way - you can even copy and paste the code refered to here

Remember to move the exercises to another link and link to answers - refer to the single entity chapter for the format.

Thank you!

Professor's comments
The examples in this chapter need to be redone using ID and IDREF to make them simpler. Some of the prior comments (e.g., defining Xpath) will not be appropriate as a result. The tour examples are a good idea to keep with the tourism theme.

rtw 17:20, 5 Feb 2006 (UTC)

post revision comments
viji this revision looks like it addressed most of the comments


text:
User:Jnothman

See my wikipedia page

title: User:Jnothman


text:
User talk:24.70.95.204

Please stop adding nonsense to Wikibooks. It is considered vandalism. If you would like to experiment, use the sandbox. Thank you.

title: User talk:24.70.95.204


text:
User talk:202.149.64.3

Please stop adding nonsense to Wikibooks. It is considered vandalism. If you would like to experiment, use the sandbox. Thank you.

title: User talk:202.149.64.3


text: User talk:69.119.244.103

title: User talk:69.119.244.103


text:
User talk:213.22.171.54

Please stop adding nonsense to Wikibooks. It is considered vandalism. If you would like to experiment, use the sandbox. Thank you.

title: User talk:213.22.171.54


text:
Reverse Engineering/File Formats

This section will talk about reverse-engineering proprietary file formats. Many software developers need to reverse engineer a proprietary file format, especially for the purposes of interoperability. For example, every year the Open Office project needs to reverse engineer the Microsoft Office file formats. Furthermore, reverse engineering is required for forensics purposes. The chapters in this section will talk about how to understand a proprietary file format.

File Header
Most file formats begin with a "header," a few bytes that describe the file type and version. Because there are several incompatible file formats with the same extension (for example, ".doc" and ".cod"), the header gives a program enough additional information to see if this file is one of the formats that program can handle.

Many programmers package their data in some sort of "container format" before writing it out to disk. If they use the standard zlib to hold their data in compressed form, the file will begin with the 2 bytes 0x1f 0x8b (in decimal, 31 139 ).

Blank Space
Some files are made up largely of blank space, for example, .ds_store files generated by OS X. Blank space will appear as a series of 0's in a hex editor. The creators of a file format may add blank space for a variety of reasons, for example, the author of this study on .ds_store files speculated that they exist to speed up writing data, as other data would not need to be pushed around to make room. They could also serve to prevent fragmentation.

For most purposes, blank space can be ignored.

Tools
File format reverse engineering is the domain of hex editors. Typically they are used more often to display file contents as opposed to editing them. Hex editors allow you to superimpose a data structure on top of the data (sometimes called custom views or similar), which are very helpful. Once a particular structure has been discovered in a file, these mechanisms can be used to document the structure, as well as to provide a more meaningful display of the information than just hex code.

Also useful are Unix/Linux tools like strings(1) and file(1).

 * strings:Finds and prints sequences of printable characters in a file. This can give hints of what data is embedded in the file.
 * file:Attempts to determine a file type. Sometimes file format designers re-use already well known file formats or file compression algorithms. There is a small but notable chance that file(1) can reveal this.

Windows ports of these tools are also available. E.g. as part of the Cygwin environment (strings is part of the binutils, don't ask ...).

Equally important as a hex editor is a brain. File format reverse engineering means to reason about what the hex editor and other tools displays. To guess structures, relations and the meaning of the data, to develop theories and then verify them. Very few tools can help here.

In a few limited cases additional tools are helpful. E.g. for checking brute-force if a particular part of the file consists of some embedded, compressed data. Typically such tools are written or scripted on the fly as custom tools. Another typical set of custom tools are the ones which are used to break up a file into separate components - once it has been discovered that a particular file indeed consists of separate parts, and how they are separated in the file. C/C++, Java, but also scripting languages like Perl are often used here (Perl because it can handle binary data, while classic Unix scripting tools are often limited to text data only).

In some cases a proprietary file format might contain executable code. For example, a firmware update file for some embedded device very likely contains executable code. Typically that code is wrapped into some structure, e.g. a file system, compressed, garnished with boot/flash code etc. In such cases a disassembler/decompiler for the particular executable format might be helpful.

Further, documentation of and familiarity with checksum algorithms, compression algorithms, encoding techniques, and also programming languages is very helpful.

Also very helpful is the availability of the application that produces and reads the proprietary file format. That application can be used to create test files, but also to verify if an own generated file is correct.

Strategies
Look for the obvious first. E.g. magic numbers, a block structure, ASCII text in the file. Anything that can be more or less identified clearly can be the entry ticket to more. Once a particular structure has been identified, look for in-file pointers to that data. E.g. if the data is referenced from some other part of the file with an absolute or relative address. It is also very important to find out the byte order (little endian or big endian).

Choosing the target
If you have access to the software that created the file, you can always create files with the contents of your choosing. This makes reverse engineering substantially easier. In cryptography terms, you are engaging in a chosen-plaintext attack.

Probing
Once you formulate theory as to what some data in the file might mean, you can verify that theory by creating a manipulated file. Replace it with some other data using a hex editor or a custom tool. Then load the manipulated file into the original application. If the application loads the file and displays the intended change, the theory is probably correct. Sometimes it is not trivial to change the application and reload it because of the defense mechanism that may be present. Some application check the hash and signature of the code before running it.

Introduction
File formats which are either in part or completely compressed, encrypted or scrambled are among the toughest nuts to crack. Of course, compression is different from encryption, and typically done for a different purpose. However, the resulting file formats often look similar: A bunch of gibberish. This is the intended result when file format designers go for encryption, but it is also often a desired side effect when compression is applied.

If checking a file with a hex editor or similar reveals that it just contains gibberish and e.g. not any easy to identify text strings, patterns or similar, it might indicate that the particular file is compressed, encrypted or scrambled. The methods for reverse engineering these files are similar. There might, however, be a big difference from a legal point of view. Many countries have laws against circumventing copy protection, and encryption can be seen as some kind of copy protection. See Reverse Engineering/Legal Aspects for some more hints regarding this, and seek qualified legal advice before attempting to reverse engineer an encrypted or otherwise protected file format. Similar issues might arise when a file format just uses scrambling. The format "owner" might argue that the scrambling is used as some kind of copy protection, encryption or whatever, and circumventing it might break some law. Again, seek qualified legal advice.

The remainder of this section only deals with reverse-engineering the compression of a file. This is typically just an initial step in the complete reverse-engineering process. Once it has been successfully decompressed, other reverse engineering methods need to be applied to identify the file contents and structure.

Well-Known Compression Algorithms
Often file format designers apply well known compression algorithms. Either in the form of even using a particular, well known implementation of a certain algorithm (a well-known tool), or by re-implementing a well known algorithm unchanged. In the easiest case this has been documented. For example, it is well documented that the OpenOffice file format uses ZIP archives, and therefore there is no point in reverse engineering that format.

Unfortunately, for many formats we don't have this documentation. In case a well-known implementation of a particular algorithm has been used it is often relatively easy to reverse engineer. Such compressed file formats tend to start with a format identifier (magic number), clearly identifying the particular compressed format. The compression tool has left its "fingerprint" in place.

{{Example|The following is a hex dump of the first few bytes of a fictitious firmware update file for a particular SOHO router 00000000 60 ea 27 00 1e 06 01 00  10 00 02 84 84 86 dc 34  | `.'............4 | 00000010 84 86 dc 34 00 00 00 00  00 00 00 00 00 00 00 00  | ...4............ | 00000020  00 00 44 54 41 2e 41 52  4a 00 00 b1 18 78 a6 00  | ..DTA.ARJ....x.. | 00000030  00 60 ea 27 00 1e 06 01  00 10 01 00 84 4c 86 dc  | .`.'.........L.. | 00000040  34 9b 17 0c 00 e8 a4 25  00 25 10 0d 10 00 00 20  | 4......%.%..... | 00000050  00 00 00 44 54 41 2e 4d  45 4d 00 00 50 98 0b 8f  | ...DTA.MEM..P... | 00000060  00 00 1f 30 84 dd 7b db  48 da 6f fd ee fd bb da  | ...0..{.H.o..... | }}

The file is compressed with ARJ. Not only does the string DTA.ARJ give it away for the human eye, but also the first two bytes 60 ea, which are known to identify ARJ-compressed files.

The Unix/linux tool file(1) is quite aware of many standard compressed file formats.

The next steps after the compression format has been discovered is obvious: To obtain a version of the used compression tool and to use it to decompress the data. The result, however, often needs more reverse engineering. For example, the above mentioned router firmware might contain separate sections for separate areas of the router's flash memory, each guarded with an own checksum.

A variant of using a well known compression algorithm and tool can also sometimes be found, which is more difficult to reverse engineer. In such a case the file is prefixed with some additional data, and the actual compression format can't be identified by just checking the file format. Lets assume, for example, another fictitious SOHO router's firmware update file, which is build as it follows:

Of course, the format can only be known once the file format has been reverse engineered. So how is that done? Well, in the fictitious case we assume that an inspection with the Unix/Linux tool strings(1) reveals the following interesting strings in the file:

The strings are very revealing, and those knowledgeable will recognize the name Mark Adler as one of the authors of zlib zlib, which is the base for info-zip as well as GNU's gzip. Those not so knowledgeable might at least have the idea to search for the name and the keyword compression.

It is a good bet to assume that at least parts of the file are ZIP compressed. Further probing might reveal that the file does not contain a complete ZIP archive, but just a section which is compressed with the ZIP deflate algorithm, and supposed to be decompressed with the ZIP inflate algorithm (likely version 1.1.3, as the output of strings revealed). Therefore, the fictitious file might be further separated into its components by using a custom tool which iteratively applies the <tt>inflate</tt> algorithm to the file, until the generated result makes some sense (e.g. until the result contains some recognizable clear text strings).

Unknown or homemade Compression Algorithms
If the software that either creates or reads the file is available then it is very possible to reverse the file format. You can use live analysis of the running application when reading/writing the file. Doing this is likely the easiest way to determine the data structure of the file.

If the software is not available, all bets are off if there is an unknown or homemade/ad-hoc compression algorithm, or a non-standard implementation of a known algorithm. One has to be exceptionally lucky to figure out the details of the applied algorithm, so the accompanying decompression algorithm can be constructed, although cryptologists strongly discourage the use of ad-hoc encryption schemes, as they typically do not stand up to serious cryptanalysis.

Sometimes additional information can be found. E.g. if a vendor has filed a patent application for a particular algorithm, or is known to have fallen in love with a particular compression technology in other products, e.g. communication protocols. Sometimes it might turn out that the file format actually belongs to some OEM or 3rd party product, and that information about that product is available.

Otherwise, there is a small chance that trial-and-error might reveal something about the file. e.g. run-length encodings are a popular, simple and easy to implement compression algorithm, so they can sometimes be found in homemade implementations. It might be worth a try to investigate if a file might be compressed that way. An investigation of a few other well known compression techniques might also be worth a try.

Last but not least, crypto-analysis techniques might reveal something interesting about the compression. E.g. reoccurring blocks of information might point to a particular compression algorithm. However, this requires a lot of effort, time and skill.

title: Reverse Engineering/File Formats


text:
Template talk:Reverse Engineering

This is a basic chapter-heading template for the Reverse Engineering Wikibook--Whiteknight 17:44, 13 October 2005 (UTC)

title: Template talk:Reverse Engineering


text:
Folkstyle Wrestling/What is Folkstyle Wrestling

Folkstyle Wrestling is the form of Wrestling that is practiced mostly in American high schools and colleges. It is also known as collegiate wrestling. The object of folkstyle wrestling is to pin your opponent. Failing this, you want to get as many points as possible.

Folkstyle scoring focuses mainly on changes in control. Taking an opponent down to the mat, escaping from or reversing control, or turning an opponent's back to the mat are all scoring situations. This differs from other styles such as Freestyle or Greco Roman, which don't encourage escapes or reversals.

The scoring for folkstyle wrestling is as follows: expose an opponents back to the mat for 3 seconds at a 45 degree angle or less, score 2 points. Hold him for 5 seconds at 45 degrees or less, and you earn 3 points. In a takedown, once you gain control of your opponents hips and they have 3 points of balance (arms and legs) on the mat, you earn 2 points. If they can reverse the position so that they have control of your hips, they earn 2 points. If they simply get away so that you both are standing with no one in control, it is called an escape and they earn one point.