├── README.md ├── assert.rex ├── aux2svg.rex ├── devisio.rex ├── idl2wsdl.rex ├── idlfile.txt ├── images └── lose-the-horse-luke.jpg ├── io.rex ├── jcl2xml.rex ├── oorexx └── oorexxXMLparser.cls ├── parsexml.rex ├── pretty.rex ├── rexxpp.rex ├── soap.rex ├── test.xml ├── testmod.rex └── testnew.rex /README.md: -------------------------------------------------------------------------------- 1 | # A REXX XML PARSER 2 | 3 | ![the big picture](./images/lose-the-horse-luke.jpg) 4 | 5 | ## Function 6 | 7 | This is a Rexx XML parser that you can append to your 8 | own Rexx source. You can then parse xml files into an 9 | in-memory model and access the model via a DOM-like 10 | API. 11 | 12 | This version has been tested on: 13 | 14 | * z/OS v2.3 TSO (using TSO/REXX) 15 | * Windows (using Regina Rexx 3.9.1) 16 | * Linux (using Regina Rexx 3.9.1) 17 | 18 | ## Installation 19 | 20 | 1. Copy the distribution library to your Rexx library. 21 | 22 | 1. Execute the REXXPP INCLUDE pre-processor by running: 23 | 24 | `REXXPP yourlib(PRETTY) PRETTYPP` 25 | 26 | This will append PARSEXML to PRETTY and create PRETTYPP. 27 | You will be prompted for the location of each include 28 | file that cannot be found. 29 | Obviously, you can just use the editor if you prefer! 30 | 31 | 1. Execute PRETTYPP to parse the TESTXML file by: 32 | 33 | `PRETTYPP yourlib(TESTXML) (NOBLANKS` 34 | 35 | If you supply any options, be sure to put a space before 36 | the opening parenthesis. A closing parenthesis is 37 | optional. 38 | 39 | 1. Repeat steps 2 and 3 for each of the other sample Rexx 40 | procedures if you want. 41 | 42 | ## Usage 43 | 44 | 1. Initialize the parser by: 45 | 46 | `call initParser [options...]` 47 | 48 | 1. Parse the XML file to build an in-memory model 49 | 50 | `returncode = parseFile('filename')` 51 | 52 | ...or... 53 | 54 | `returncode = parseString('xml in a string')` 55 | 56 | 1. Navigate the in-memory model with the DOM API. For 57 | example: 58 | 59 | ```rexx 60 | say 'The document element is called', 61 | getName(getDocumentElement()) 62 | say 'Children of the document element are:' 63 | node = getFirstChild(getDocumentElement()) 64 | do while node <> '' 65 | if isElementNode(node) 66 | then say 'Element node:' getName(node) 67 | else say ' Text node:' getText(node) 68 | node = getNextSibling(node) 69 | end 70 | ``` 71 | 72 | 1. Optionally, destroy the in-memory model: 73 | 74 | `call destroyParser` 75 | 76 | ## Input 77 | 78 | The input to the parser consists of either an XML file or a string containing: 79 | 80 | 1. An optional XML prolog: 81 | 82 | * 0 or 1 XML declaration: 83 | 84 | ```xml 85 | 86 | ``` 87 | 88 | * 0 or more comments, Processing Instructions (PI's), and whitespace: 89 | 90 | ```xml 91 | 92 | 93 | ``` 94 | 95 | * 0 or 1 document type declaration. Formats: 96 | 97 | ```xml 98 | 99 | 100 | 101 | ``` 102 | 103 | 1. An XML body: 104 | 105 | * 1 Document element containing 0 or more child 106 | elements. For example: 107 | 108 | ```xml 109 | 110 | Text of doc element 111 | 112 | Text of child1 element 113 | 114 | More text of doc element 115 | 116 | 117 | Even more text of doc element 118 | 119 | ``` 120 | 121 | * Elements may contain: 122 | 123 | * Unparsed character data: 124 | 125 | ```xml 126 | 127 | ``` 128 | 129 | * Entity references: 130 | 131 | ```xml 132 | &name; 133 | ``` 134 | 135 | * Character references: 136 | 137 | ```xml 138 | &#nnnnn; 139 | &#xXXXX; 140 | ``` 141 | 142 | 1. An XML epilog (which is ignored): 143 | 144 | * 0 or more comments, Processing Instructions (PIs), and whitespace. 145 | 146 | ## Application Programming Interface 147 | 148 | 1. The basic setup/teardown API calls are: 149 | 150 | `initParser [options]` 151 | 152 | Initialises the parser's global variables and 153 | remembers any runtime options you specify. The 154 | options recognized are: 155 | 156 | | Option | Meaning | 157 | | -------- | ------------------------------ | 158 | | NOBLANKS | Suppress whitespace-only nodes | 159 | | DEBUG | Display some debugging info | 160 | | DUMP | Display the parse tree | 161 | 162 | `parseFile(filename)` 163 | 164 | Parses the XML data in the specified filename and 165 | builds an in-memory model that can be accessed via 166 | the DOM API (see below). 167 | 168 | `parseString(text)` 169 | 170 | Parses the XML data in the specified string. 171 | 172 | `destroyParser` 173 | 174 | Destroys the in-memory model and miscellaneous 175 | global variables. 176 | 177 | 1. In addition, the following utility API calls can be 178 | used: 179 | 180 | `removeWhitespace(text)` 181 | 182 | Returns the supplied text string but with all 183 | whitespace characters removed, multiple spaces 184 | replaced with single spaces, and leading and 185 | trailing spaces removed. 186 | 187 | `removeQuotes(text)` 188 | 189 | Returns the supplied text string but with any 190 | enclosing apostrophes or double-quotes removed. 191 | 192 | `escapeText(text)` 193 | 194 | Returns the supplied text string but with special 195 | characters encoded (for example, `<` becomes `<`) 196 | 197 | `toString(node)` 198 | 199 | Walks the document tree (beginning at the specified 200 | node) and returns a string in XML format. 201 | 202 | ## DOM API 203 | 204 | The DOM (or DOM-like) calls that you can use are 205 | listed below: 206 | 207 | 1. Document query/navigation API calls 208 | 209 | `getRoot()` 210 | 211 | Returns the node number of the root node. This 212 | can be used in calls requiring a node argument. 213 | In this implementation, `getDocumentElement()` and 214 | `getRoot()` are (incorrectly) synonymous - this may 215 | change, so you should use `getDocumentElement()` 216 | in preference to `getRoot()`. 217 | 218 | `getDocumentElement()` 219 | 220 | Returns the node number of the document element. 221 | The document element is the topmost element node. 222 | You should use this in preference to `getRoot()` 223 | (see above). 224 | 225 | `getName(node)` 226 | 227 | Returns the name of the specified node. 228 | 229 | `getNodeValue(node)` or `getText(node)` 230 | 231 | Returns the text content of an unnamed node. A 232 | node without a name can only contain text. It 233 | cannot have attributes or children. 234 | 235 | `getAttributeCount(node)` 236 | 237 | Returns the number of attributes present on the 238 | specified node. 239 | 240 | `getAttributeMap(node)` 241 | 242 | Builds a map of the attributes of the specified 243 | node. The map can be accessed via the following 244 | variables: 245 | 246 | | Variable | Content | 247 | | ----------------- | ------------------------------- | 248 | | g.0ATTRIBUTE.0 | The number of attributes mapped | 249 | | g.0ATTRIBUTE.n | The name of attribute number `n` (in order of appearance) where `n` > 0 | 250 | | g.0ATTRIBUTE.name | The value of the attribute called `name` | 251 | 252 | `getAttributeName(node,n)` 253 | 254 | Returns the name of the nth attribute of the 255 | specified node (1 is first, 2 is second, etc). 256 | 257 | `getAttributeNames(node)` 258 | 259 | Returns a space-delimited list of the names of the 260 | attributes of the specified node. 261 | 262 | `getAttribute(node,name)` 263 | 264 | Returns the value of the attribute called `name` of 265 | the specified node. 266 | 267 | `getAttribute(node,n)` 268 | 269 | Returns the value of the `n`th attribute of the 270 | specified node (1 is first, 2 is second, etc). 271 | 272 | `setAttribute(node,name,value)` 273 | 274 | Updates the value of the attribute called 'name' 275 | of the specified node. If no attribute exists with 276 | that name, then one is created. 277 | 278 | `setAttributes(node,name1,value1,name2,value2,...)` 279 | 280 | Updates the attributes of the specified node. Zero 281 | or more name/value pairs are be specified as the 282 | arguments. 283 | 284 | `hasAttribute(node,name)` 285 | 286 | Returns 1 if the specified node has an attribute 287 | with the specified name, else 0. 288 | 289 | `getParentNode(node)` or `getParent(node)` 290 | 291 | Returns the node number of the specified node's 292 | parent. If the node number returned is 0, then the 293 | specified node is the root node. 294 | All nodes have a parent (except the root node). 295 | 296 | `getFirstChild(node)` 297 | 298 | Returns the node number of the specified node's 299 | first child node. 300 | 301 | `getLastChild(node)` 302 | 303 | Returns the node number of the specified node's 304 | last child node. 305 | 306 | `getChildNodes(node)` or `getChildren(node)` 307 | 308 | Returns a space-delimited list of node numbers of 309 | the children of the specified node. You can use 310 | this list to step through the children as follows: 311 | 312 | ```rexx 313 | children = getChildren(node) 314 | say 'Node' node 'has' words(children) 'children' 315 | do i = 1 to words(children) 316 | child = word(children,i) 317 | say 'Node' child 'is' getName(child) 318 | end 319 | ``` 320 | 321 | `getChildrenByName(node,name)` 322 | 323 | Returns a space-delimited list of node numbers of 324 | the immediate children of the specified `node` which 325 | are called `name`. Names are case-sensitive. 326 | 327 | `getElementsByTagName(node,name)` 328 | 329 | Returns a space-delimited list of node numbers of 330 | the descendants of the specified `node` which are 331 | called `name`. Names are case-sensitive. 332 | 333 | `getNextSibling(node)` 334 | 335 | Returns the node number of the specified node's 336 | next sibling node. That is, the next node sharing 337 | the same parent. 338 | 339 | `getPreviousSibling(node)` 340 | 341 | Returns the node number of the specified node's 342 | previous sibling node. That is, the previous node 343 | sharing the same parent. 344 | 345 | `getProcessingInstruction(name)` 346 | 347 | Returns the value of the Processing Instruction (PI) with the specified 348 | target name. 349 | 350 | `getProcessingInstructionList()` 351 | 352 | Returns a space-delimited list of the names of all 353 | PI target names. 354 | 355 | `getNodeType(node)` 356 | 357 | Returns a number representing the specified node's 358 | type. The possible values can be compared to the 359 | following global variables: 360 | 361 | | Variable | Content | 362 | | ------------------------------ | ------- | 363 | | g.0ELEMENT_NODE | 1 | 364 | | g.0ATTRIBUTE_NODE | 2 | 365 | | g.0TEXT_NODE | 3 | 366 | | g.0CDATA_SECTION_NODE | 4 | 367 | | g.0ENTITY_REFERENCE_NODE | 5 | 368 | | g.0ENTITY_NODE | 6 | 369 | | g.0PROCESSING_INSTRUCTION_NODE | 7 | 370 | | g.0COMMENT_NODE | 8 | 371 | | g.0DOCUMENT_NODE | 9 | 372 | | g.0DOCUMENT_TYPE_NODE | 10 | 373 | | g.0DOCUMENT_FRAGMENT_NODE | 11 | 374 | | g.0NOTATION_NODE | 12 | 375 | 376 | Note: as this exposes internal implementation 377 | details, it is best not to use this routine. 378 | Consider using `isTextNode()` etc instead (see below). 379 | 380 | `isCDATA(node)` 381 | 382 | Returns 1 if the specified node is an unparsed 383 | character data (CDATA) node, else 0. CDATA nodes 384 | are used to contain content that you do not want 385 | to be treated as XML data. For example, HTML data. 386 | 387 | `isElementNode(node)` 388 | 389 | Returns 1 if the specified node is an element node, 390 | else 0. 391 | 392 | `isTextNode(node)` 393 | 394 | Returns 1 if the specified node is a text node, 395 | else 0. 396 | 397 | `isCommentNode(node)` 398 | 399 | Returns 1 if the specified node is a comment node, 400 | else 0. Note: when a document is parsed, comment 401 | nodes are ignored. This routine returns 1 iff a 402 | comment node has been inserted into the in-memory 403 | document tree by using `createComment()`. 404 | 405 | `hasChildren(node)` 406 | 407 | Returns 1 if the specified node has one or more 408 | child nodes, else 0. 409 | 410 | `getDocType(doctype)` 411 | 412 | Gets the text of the `` prolog node. 413 | 414 | 1. Document creation/mutation API calls 415 | 416 | `createDocument(name)` 417 | 418 | Returns the node number of a new document node 419 | with the specified name. 420 | 421 | `createDocumentFragment(name)` 422 | 423 | Returns the node number of a new document fragment 424 | node with the specified name. 425 | 426 | `createElement(name)` 427 | 428 | Returns the node number of a new empty element 429 | node with the specified name. An element node can 430 | have child nodes. 431 | 432 | `createTextNode(data)` 433 | 434 | Returns the node number of a new text node. A text 435 | node can *not* have child nodes. 436 | 437 | `createCDATASection(data)` 438 | 439 | Returns the node number of a new Character Data 440 | (CDATA) node. A CDATA node can *not* have child 441 | nodes. CDATA nodes are used to contain content 442 | that you do not want to be treated as XML data. 443 | For example, HTML data. 444 | 445 | `createComment(data)` 446 | 447 | Returns the node number of a new comment node. 448 | A comment node can *not* have child nodes. 449 | 450 | `appendChild(node,parent)` 451 | 452 | Appends the specified node to the end of the list 453 | of children of the specified parent node. 454 | 455 | `insertBefore(node,refnode)` 456 | 457 | Inserts node `node` before the reference node 458 | `refnode`. 459 | 460 | `removeChild(node)` 461 | 462 | Removes the specified node from its parent and 463 | returns its node number. The removed child is now 464 | an orphan. 465 | 466 | `replaceChild(newnode,oldnode)` 467 | 468 | Replaces the old child `oldnode` with the new 469 | child `newnode` and returns the old child's node 470 | number. The old child is now an orphan. 471 | 472 | `setAttribute(node,attrname,attrvalue)` 473 | 474 | Adds or replaces the attribute called `attrname` 475 | on the specified node with the value `attrvalue`. 476 | 477 | `removeAttribute(node,attrname)` 478 | 479 | Removes the attribute called `attrname` from the 480 | specified node. 481 | 482 | `setDocType(doctype)` 483 | 484 | Sets the text of the `` prolog node. 485 | 486 | `cloneNode(node,[deep])` 487 | 488 | Creates a copy (a clone) of the specified node 489 | and returns its node number. If deep = 1 then 490 | all descendants of the specified node are also 491 | cloned, else only the specified node and its 492 | attributes are cloned. 493 | 494 | ## NOTES 495 | 496 | 1. This parser creates global variables and so its 497 | operation may be severely jiggered if you update 498 | any of them accidentally (or on purpose). The 499 | variables you should avoid updating yourself are: 500 | 501 | | REXX Variable | 502 | | ----------------- | 503 | | g.0ATTRIBUTE.n | 504 | | g.0ATTRIBUTE.name | 505 | | g.0ATTRSOK | 506 | | g.0DTD | 507 | | g.0ENDOFDOC | 508 | | g.0ENTITIES | 509 | | g.0ENTITY.name | 510 | | g.0FIRST.n | 511 | | g.0LAST.n | 512 | | g.0NAME.n | 513 | | g.0NEXT.n | 514 | | g.0NEXTID | 515 | | g.0OPTION.name | 516 | | g.0OPTIONS | 517 | | g.0PARENT.n | 518 | | g.0PI | 519 | | g.0PI.name | 520 | | g.0PREV.n | 521 | | g.0PUBLIC | 522 | | g.0ROOT | 523 | | g.0STACK | 524 | | g.0SYSTEM | 525 | | g.0TEXT.n | 526 | | g.0TYPE.n | 527 | | g.0WHITESPACE | 528 | | g.0XML | 529 | | g.?XML | 530 | | g.?XML.VERSION | 531 | | g.?XML.ENCODING | 532 | | g.?XML.STANDALONE | 533 | 534 | 1. To reduce the incidence of name clashes, procedure 535 | names that are not meant to be part of the public 536 | API have been prefixed with '_'. 537 | -------------------------------------------------------------------------------- /assert.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - ASSERT ** 33 | ** ** 34 | ** FUNCTION - Unit test assertion plumbing. ** 35 | ** ** 36 | ** ** 37 | ** SYNTAX - n/a ** 38 | ** ** 39 | ** NOTES - 1. You will have to either append the ASSERT rexx code ** 40 | ** manually to your Rexx source, or run your Rexx ** 41 | ** source through the REXXPP rexx pre-processor. ** 42 | ** ** 43 | ** To use the pre-processor, run: ** 44 | ** ** 45 | ** rexxpp yourrexx xyz ** 46 | ** ** 47 | ** ...and then run the resulting 'xyz' rexx procedure: ** 48 | ** ** 49 | ** xyz ** 50 | ** ** 51 | ** ** 52 | ** AUTHOR - Andrew J. Armstrong ** 53 | ** ** 54 | ** HISTORY - Date By Reason (most recent at the top please) ** 55 | ** -------- --- ----------------------------------------- ** 56 | ** 20090822 AJA Changed from GPL to BSD license. ** 57 | ** 20060803 AJA Added message number to messages. ** 58 | ** 20050517 AJA Initial version. ** 59 | ** ** 60 | **********************************************************************/ 61 | 62 | parse source . . sSourceFile . 63 | parse value sourceline(1) with . sVersion 64 | say 'Unit test routines' sVersion 65 | say 'You cannot invoke this rexx by itself!' 66 | say 67 | say 'This rexx is a collection of subroutines to be called' 68 | say 'from your own rexx procedures. You should either:' 69 | say ' - Append this procedure to your own rexx procedure,' 70 | say ' or,' 71 | say ' - Append the following line to your rexx:' 72 | say ' /* INCLUDE' sSourceFile '*/' 73 | say ' ...and run the rexx preprocessor:' 74 | say ' rexxpp myrexx myrexxpp' 75 | say ' This will create myrexxpp by appending this file to myrexx' 76 | exit 77 | 78 | assertEquals: procedure 79 | parse arg expected,actual,message 80 | if actual <> expected 81 | then call failNotEquals expected,actual,message 82 | return 83 | 84 | assertTrue: procedure 85 | parse arg condition,message 86 | if condition = 0 87 | then call failNotEquals 1,condition,message 88 | return 89 | 90 | assertFalse: procedure 91 | parse arg condition,message 92 | if condition = 1 93 | then call failNotEquals 0,condition,message 94 | return 95 | 96 | failNotEquals: procedure: 97 | parse arg expected,actual,message 98 | reason = 'expected:<'expected'> but was:<'actual'>' 99 | if message = '' 100 | then say 'ASS001E Assertion failed:' reason 101 | else say 'ASS002E Assertion failed:' message reason 102 | return 103 | -------------------------------------------------------------------------------- /aux2svg.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | 3 | CICS Auxiliary Trace Visualizer V2.0 4 | Copyright (C) 2005-2020 Andrew J. Armstrong 5 | 6 | This example is now hosted separately at 7 | 8 | https://github.com/abend0c1/aux2svg 9 | 10 | */ -------------------------------------------------------------------------------- /devisio.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - DEVISIO ** 33 | ** ** 34 | ** FUNCTION - Removes non-SVG markup from SVG documents created by ** 35 | ** Microsoft Visio. The Microsoft-specific extensions are ** 36 | ** identified by the 'v:' name space. ** 37 | ** ** 38 | ** USAGE - You can run this Rexx on an IBM mainframe, or on a PC ** 39 | ** by using Regina Rexx from: ** 40 | ** ** 41 | ** http://regina-rexx.sourceforge.net ** 42 | ** ** 43 | ** ** 44 | ** SYNTAX - DEVISIO infile outfile [(options...] ** 45 | ** ** 46 | ** Where, ** 47 | ** infile = Microsoft Visio SVG file. ** 48 | ** outfile = SVG file. ** 49 | ** options = Options ** 50 | ** ** 51 | ** NOTES - 1. You will have to either append the PARSEXML and ** 52 | ** PRETTY source files manually to this file, or run ** 53 | ** this file through the REXX rexx pre-processor. ** 54 | ** ** 55 | ** To use the pre-processor, run: ** 56 | ** ** 57 | ** tso rexxpp your.rexx.lib(devisio) ** 58 | ** ** 59 | ** ** 60 | ** AUTHOR - Andrew J. Armstrong ** 61 | ** ** 62 | ** HISTORY - Date By Reason (most recent at the top please) ** 63 | ** -------- --- ----------------------------------------- ** 64 | ** 20090822 AJA Changed from GPL to BSD license. ** 65 | ** 20051026 AJA Initial version. ** 66 | ** ** 67 | **********************************************************************/ 68 | 69 | parse arg sFileIn sFileOut' ('sOptions')' 70 | 71 | numeric digits 16 72 | parse value sourceline(1) with . sVersion 73 | say 'VIS000I Microsoft Visio SVG extensions remover' sVersion 74 | if sFileIn = '' 75 | then do 76 | say 'Syntax:' 77 | say ' devisio infile outfile (options' 78 | say 79 | say 'Where:' 80 | say ' infile = Microsoft Visio SVG file' 81 | say ' outfile = SVG file (without Microsoft extensions)' 82 | exit 83 | end 84 | 85 | sOptions = 'NOBLANKS' translate(sOptions) 86 | call initParser sOptions /* DO THIS FIRST! Sets g. vars to '' */ 87 | 88 | parse source g.0ENV . 89 | if g.0ENV = 'TSO' 90 | then do 91 | address ISPEXEC 92 | 'CONTROL ERRORS RETURN' 93 | g.0LINES = 0 94 | end 95 | 96 | call Prolog 97 | 98 | /* Open the specified file and parse it */ 99 | nParseRC = parseFile(sFileIn) 100 | doc = getDocumentElement() 101 | 102 | g.0PREFIXES = getVisioNamespacePrefixes(doc) 103 | say 'VIS002I Removing elements and attributes with prefixes:', 104 | g.0PREFIXES 105 | 106 | call removeVisioTags doc 107 | 108 | call findAllReferences doc 109 | 110 | call removeUnusedReferences doc 111 | 112 | /* fix ups to make the svg render properly...*/ 113 | call setAttributes doc,, 114 | 'xml:space','default',, 115 | 'xmlns:xlink','http://www.w3.org/1999/xlink' 116 | 117 | call prettyPrinter sFileOut 118 | 119 | call Epilog 120 | exit 121 | 122 | getVisioNamespacePrefixes: procedure expose g. 123 | parse arg doc 124 | sAttrNames = getAttributeNames(doc) 125 | sPrefixes = '' 126 | do i = 1 to words(sAttrNames) 127 | sAttrName = word(sAttrNames,i) 128 | if left(sAttrName,6) = 'xmlns:' 129 | then do 130 | sNameSpace = getAttribute(doc,sAttrName) 131 | if pos('schemas.microsoft.com',sNameSpace) > 0 132 | then do 133 | sPrefix = substr(sAttrName,7) 134 | sPrefixes = sPrefixes sPrefix 135 | say 'VIS001I Removing' sAttrName'='getAttribute(doc,sAttrName) 136 | call removeAttribute doc,sAttrName 137 | end 138 | end 139 | end 140 | return strip(sPrefixes) 141 | 142 | removeVisioTags: procedure expose g. 143 | parse arg node 144 | sTagName = getNodeName(node) 145 | if isVisioExtension(sTagName) 146 | then call removeChild node 147 | else do 148 | if sTagName = 'marker' /* HACK: fixes Visio 2003 bug */ 149 | then call setAttribute node,'overflow','visible' 150 | sAttrNames = getAttributeNames(node) 151 | do i = 1 to words(sAttrNames) 152 | sAttrName = word(sAttrNames,i) 153 | if isVisioExtension(sAttrName) 154 | then call removeAttribute node,sAttrName 155 | end 156 | children = getChildNodes(node) 157 | do i = 1 to words(children) 158 | child = word(children,i) 159 | call removeVisioTags child 160 | end 161 | end 162 | return 163 | 164 | isVisioExtension: procedure expose g. 165 | parse arg sTagName . 166 | if wordpos(sTagName,'title desc') > 0 then return 1 167 | if pos(':',sTagName) = 0 then return 0 168 | parse arg sPrefix':' 169 | if wordpos(sPrefix,g.0PREFIXES) > 0 then return 1 170 | return 0 171 | 172 | findAllReferences: procedure expose g. 173 | parse arg node 174 | sText = getNodeValue(node) 175 | do while pos('url(#',sText) > 0 176 | parse var sText 'url(#'sId')'sText 177 | sRef = '#'sId 178 | g.0ID.sRef = 1 179 | end 180 | if hasAttribute(node,'xlink:href') 181 | then do 182 | sId = getAttribute(node,'xlink:href') 183 | if left(sId,1) = '#' /* is it a local reference? */ 184 | then g.0ID.sId = 1 /* For example: g.0ID.#mrkr13-14 = 1 */ 185 | end 186 | children = getChildNodes(node) 187 | do i = 1 to words(children) 188 | child = word(children,i) 189 | call findAllReferences child 190 | end 191 | return 192 | 193 | removeUnusedReferences: procedure expose g. 194 | parse arg node 195 | if hasAttribute(node,'id') 196 | then do 197 | sId = getAttribute(node,'id') 198 | sRef = '#'sId 199 | if g.0ID.sRef <> 1 200 | then call removeAttribute node,'id' 201 | end 202 | children = getChildNodes(node) 203 | do i = 1 to words(children) 204 | child = word(children,i) 205 | call removeUnusedReferences child 206 | end 207 | return 208 | 209 | 210 | Prolog: procedure expose g. 211 | return 212 | 213 | Epilog: procedure expose g. 214 | return 215 | 216 | /*INCLUDE pretty.rex */ 217 | -------------------------------------------------------------------------------- /idl2wsdl.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - IDL2WSDL ** 33 | ** ** 34 | ** FUNCTION - Converts an EntireX Interface Definition Language (IDL)** 35 | ** file into a Web Services Description Language (WSDL) ** 36 | ** file. It may be useful for sites that want to ** 37 | ** re-implement an EntireX application as a Web Service. ** 38 | ** It takes the tedium of converting the relatively easy ** 39 | ** to understand IDL file format into the hideously ** 40 | ** complex WSDL file format. ** 41 | ** ** 42 | ** ** 43 | ** USAGE - You can run this Rexx on an IBM mainframe, or on a PC ** 44 | ** by using Regina Rexx from: ** 45 | ** ** 46 | ** http://regina-rexx.sourceforge.net ** 47 | ** ** 48 | ** ** 49 | ** SYNTAX IDL2WSDL infile [url [ns [ (options [)] ]]]' ** 50 | ** ** 51 | ** Where, ** 52 | ** infile = Name of your EntireX Interface Definition ** 53 | ** Language (IDL) file. For example: ** 54 | ** example.idl ** 55 | ** url = URL of the service. For example: ** 56 | ** http://10.1.2.3:8080/cics/cwba/soapima ** 57 | ** ns = Namespace of the service. For example: ** 58 | ** http://myservice.example.org ** 59 | ** options = RPC - Remote Procedure Call (style) ** 60 | ** DOCUMENT - XML document (style) ** 61 | ** ENCODED - Parameters defined inline (use) ** 62 | ** LITERAL - Parameters defined by schema (use)** 63 | ** WRAPPED - Special case of DOCUMENT LITERAL ** 64 | ** XML - Create XML file (for debugging) ** 65 | ** ** 66 | ** Valid style and use combinations are: ** 67 | ** WRAPPED <-- This is the default ** 68 | ** DOCUMENT LITERAL ** 69 | ** RPC LITERAL ** 70 | ** RPC ENCODED ** 71 | ** ** 72 | ** NOTES - 1. This Rexx uses the Rexx XML parser in CBT FILE 647 ** 73 | ** from www.cbttape.org. ** 74 | ** You will have to either append the PARSEXML and ** 75 | ** PRETTY source files manually to this file, or run ** 76 | ** this file through the REXX rexx pre-processor. ** 77 | ** ** 78 | ** To use the pre-processor on TSO, run: ** 79 | ** ** 80 | ** tso rexxpp your.rexx.lib(idl2wsdl) ** 81 | ** ** 82 | ** To use the pre-processor on Windows, run: ** 83 | ** ** 84 | ** rexx rexxpp idl2wsdl.rexx idl2wsdl.new ** 85 | ** ** 86 | ** ...and then rename the .new file to .rexx ** 87 | ** ** 88 | ** AUTHOR - Andrew J. Armstrong ** 89 | ** ** 90 | ** HISTORY - Date By Reason (most recent at the top please) ** 91 | ** -------- --- ----------------------------------------- ** 92 | ** 20090822 AJA Changed from GPL to BSD license. ** 93 | ** 20060525 AJA Update documentation. ** 94 | ** 20060216 AJA User must supply URL & namespace. ** 95 | ** 20051106 AJA Support RPC/DOC + ENC/LIT/WRAPPED. ** 96 | ** 20051102 AJA Intial version. ** 97 | ** ** 98 | **********************************************************************/ 99 | 100 | parse arg sFileIn sURL sNamespace' ('sOptions')' 101 | 102 | numeric digits 16 103 | parse value sourceline(1) with . sVersion 104 | say 'IDL000I EntireX IDL to WSDL File Converter' sVersion 105 | if sFileIn = '' 106 | then do 107 | say 'Syntax:' 108 | say ' IDL2WSDL infile url ns (options' 109 | say 110 | say 'Where:' 111 | say ' infile = EntireX IDL input file. For example:' 112 | say ' example.idl' 113 | say ' url = URL of the service. For example:' 114 | say ' http://10.1.2.3:8080/cics/cwba/soapima' 115 | say ' ns = Namespace of the service. For example:' 116 | say ' http://myservice.example.org' 117 | say ' options = RPC - Remote Procedure Call (style)' 118 | say ' DOCUMENT - XML document (style)' 119 | say ' ENCODED - Parameters defined inline (use)' 120 | say ' LITERAL - Parameters defined by schema (use)' 121 | say ' WRAPPED - Special case of DOCUMENT LITERAL' 122 | say ' XML - Create XML file (for debugging)' 123 | say 124 | say ' Valid option combinations are:' 125 | say ' WRAPPED' 126 | say ' DOCUMENT LITERAL' 127 | say ' RPC LITERAL' 128 | say ' RPC ENCODED' 129 | exit 130 | end 131 | say 'IDL001I Reading EntireX IDL file in' sFileIn 132 | 133 | 134 | sOptions = 'NOBLANKS' translate(sOptions) 135 | call initParser sOptions /* DO THIS FIRST! Sets g. vars to '' */ 136 | call setDocType /* we don't need a doctype declaration */ 137 | 138 | g.0FILEIDL = sFileIn 139 | g.0URL = prompt(sURL,, 140 | 'Enter URL of this service',, 141 | 'http://10.1.2.3:8080/cics/cwba/soapima/') 142 | g.0NAMESPACE = prompt(sNamespace,, 143 | 'Enter XML namespace of this service',, 144 | 'http://myservice.example.org') 145 | 146 | parse source g.0ENV . 147 | if g.0ENV = 'TSO' 148 | then do 149 | address ISPEXEC 150 | 'CONTROL ERRORS RETURN' 151 | g.0LINES = 0 152 | end 153 | 154 | call setOptions sOptions 155 | call Prolog 156 | 157 | /* Read the IDL file into an in-memory XML document */ 158 | idl = scanEntireXIdlFile() 159 | 160 | sFileName = getFilenameWithoutExtension(sFileIn) 161 | libraries = getChildNodes(idl) 162 | do i = 1 to words(libraries) 163 | library = word(libraries,i) 164 | sLibrary = getAttribute(library,'name') 165 | call createWSDL sFileName'.'sLibrary,library 166 | end 167 | 168 | call Epilog 169 | exit 170 | 171 | prompt: procedure expose g. 172 | parse arg sReply,sPrompt,sDefault 173 | if sReply = '' 174 | then do 175 | say 'IDL000R' sPrompt '['sDefault']:' 176 | parse pull sReply 177 | if sReply = '' then sReply = sDefault 178 | end 179 | return sReply 180 | 181 | 182 | /* 183 | 191 | . 192 | . 193 | 194 | */ 195 | createWSDL: procedure expose g. 196 | parse arg sFile,node 197 | if g.0OPTION.XML 198 | then call prettyPrinter sFile'.xml',,node 199 | 200 | /* Build the high-level WSDL file structure... */ 201 | g.0DEFS = createElement('wsdl:definitions') 202 | g.0TYPES = createElement('wsdl:types') 203 | g.0PORTTYPE = createElement('wsdl:portType') 204 | g.0BINDING = createElement('wsdl:binding') 205 | g.0SERVICE = createElement('wsdl:service') 206 | 207 | /* The porttype, binding and service elements are more-or-less the 208 | same for all combinations of style and use, so build them now... */ 209 | call defineDefinitions node 210 | call defineTypes node 211 | call definePortType node 212 | call defineBinding node 213 | call defineService node 214 | call appendChild g.0TYPES,g.0DEFS 215 | 216 | 217 | /* Now add message elements depending on style and use... */ 218 | select 219 | when g.0OPTION.WRAPPED then, 220 | call createDocWrapped node 221 | when g.0OPTION.DOCUMENT & g.0OPTION.LITERAL then, 222 | call createDocLiteral node 223 | when g.0OPTION.RPC & g.0OPTION.LITERAL then, 224 | call createRpcLiteral node 225 | when g.0OPTION.RPC & g.0OPTION.ENCODED then, 226 | call createRpcEncoded node 227 | otherwise, 228 | call createDocWrapped node 229 | end 230 | 231 | call appendChild g.0PORTTYPE,g.0DEFS 232 | call appendChild g.0BINDING,g.0DEFS 233 | call appendChild g.0SERVICE,g.0DEFS 234 | 235 | /* Serialise the WSDL document to a file... */ 236 | call prettyPrinter sFile'.wsdl',,g.0DEFS 237 | return 238 | 239 | 240 | defineDefinitions: procedure expose g. 241 | parse arg node 242 | call setAttributes g.0DEFS,, 243 | 'targetNamespace',g.0NAMESPACE,, 244 | 'xmlns:impl',g.0NAMESPACE,, 245 | 'xmlns:intf',g.0NAMESPACE,, 246 | 'xmlns:wsdl','http://schemas.xmlsoap.org/wsdl/',, 247 | 'xmlns:wsdlsoap','http://schemas.xmlsoap.org/wsdl/soap/',, 248 | 'xmlns:xsd','http://www.w3.org/2001/XMLSchema' 249 | 250 | if g.0OPTION.ENCODED 251 | then call setAttribute g.0DEFS,, 252 | 'xmlns:soapenc','http://schemas.xmlsoap.org/soap/encoding/' 253 | 254 | call appendChild createComment('Created by EntireX IDL-to-WSDL', 255 | 'converter V1.0 on' date() time() userid()),g.0DEFS 256 | 257 | if g.0OPTION.WRAPPED 258 | then call appendChild createComment('Style='getStyle() '(wrapped)', 259 | 'Use='getUse()),g.0DEFS 260 | else call appendChild createComment('Style='getStyle(), 261 | 'Use='getUse()),g.0DEFS 262 | return 263 | 264 | /* 265 | 266 | 268 | 270 | 271 | 272 | 274 | 276 | 278 | 280 | 282 | 283 | 284 | 285 | 286 | */ 287 | defineTypes: procedure expose g. 288 | parse arg node 289 | g.0SCHEMA = createElement('schema') 290 | call appendChild g.0SCHEMA,g.0TYPES 291 | if g.0OPTION.DOCUMENT | g.0OPTION.WRAPPED 292 | then call setAttribute g.0SCHEMA,'elementFormDefault','qualified' 293 | call setAttributes g.0SCHEMA,, 294 | 'targetNamespace',g.0NAMESPACE,, 295 | 'xmlns','http://www.w3.org/2001/XMLSchema' 296 | if g.0OPTION.ENCODED 297 | then do 298 | import = createElement('import') 299 | call appendChild import,g.0SCHEMA 300 | call setAttribute import,, 301 | 'namespace','http://schemas.xmlsoap.org/soap/encoding/' 302 | end 303 | structuresnode = getChildrenByName(node,'structures') 304 | structures = getChildren(structuresnode) 305 | do i = 1 to words(structures) 306 | struct = word(structures,i) 307 | call appendComplexType struct,g.0SCHEMA 308 | end 309 | return 310 | 311 | /* 312 | 313 | 315 | 317 | 318 | */ 319 | definePortType: procedure expose g. 320 | parse arg node 321 | sService = getAttribute(node,'name') 322 | call setAttribute g.0PORTTYPE,'name',sService 323 | programs = getChildrenByName(node,'programs') 324 | if programs <> '' 325 | then do 326 | programs = getChildren(programs) 327 | do i = 1 to words(programs) 328 | program = word(programs,i) 329 | sOperation = getAttribute(program,'name') 330 | operation = createElement('wsdl:operation') 331 | call appendChild operation,g.0PORTTYPE 332 | call setAttribute operation,'name',sOperation 333 | input = createElement('wsdl:input') 334 | call appendChild input,operation 335 | call setAttributes input,, 336 | 'message','impl:'sOperation'Request',, 337 | 'name',sOperation'Request' 338 | output = createElement('wsdl:output') 339 | call appendChild output,operation 340 | call setAttributes output,, 341 | 'message','impl:'sOperation'Response',, 342 | 'name',sOperation'Response' 343 | end 344 | end 345 | return 346 | 347 | /* 348 | 350 | 352 | 353 | 354 | 355 | 359 | 360 | 361 | 365 | 366 | 367 | . 368 | . 369 | 370 | */ 371 | defineBinding: procedure expose g. 372 | parse arg node 373 | sService = getAttribute(node,'name') 374 | call setAttributes g.0BINDING,, 375 | 'name',sService'SoapBinding',, 376 | 'type','impl:'sService 377 | soapbinding = createElement('wsdlsoap:binding') 378 | call appendChild soapbinding,g.0BINDING 379 | call setAttributes soapbinding,, 380 | 'style',getStyle(),, 381 | 'transport','http://schemas.xmlsoap.org/soap/http' 382 | programs = getChildrenByName(node,'programs') 383 | if programs <> '' 384 | then do 385 | programs = getChildren(programs) 386 | do i = 1 to words(programs) 387 | program = word(programs,i) 388 | sOperation = getAttribute(program,'name') 389 | operation = createElement('wsdl:operation') 390 | call setAttribute operation,'name',sOperation 391 | call appendChild operation,g.0BINDING 392 | soapoperation = createElement('wsdlsoap:operation') 393 | call appendChild soapoperation,operation 394 | call setAttribute soapoperation,'soapAction','' 395 | 396 | input = createElement('wsdl:input') 397 | call appendChild input,operation 398 | call setAttribute input,'name',sOperation'Request' 399 | body = createElement('wsdlsoap:body') 400 | call appendChild body,input 401 | if g.0OPTION.ENCODED 402 | then call setAttribute body,, 403 | 'encodingStyle','http://schemas.xmlsoap.org/soap/encoding/' 404 | call setAttributes body,, 405 | 'namespace',g.0NAMESPACE,, 406 | 'use',getUse() 407 | 408 | output = createElement('wsdl:output') 409 | call appendChild output,operation 410 | call setAttribute output,'name',sOperation'Response' 411 | body = createElement('wsdlsoap:body') 412 | call appendChild body,output 413 | if g.0OPTION.ENCODED 414 | then call setAttribute body,, 415 | 'encodingStyle','http://schemas.xmlsoap.org/soap/encoding/' 416 | if g.0OPTION.RPC 417 | then call setAttribute body,'namespace',g.0NAMESPACE 418 | call setAttribute body,'use',getUse() 419 | end 420 | end 421 | return 422 | 423 | getStyle: procedure expose g. 424 | if g.0OPTION.RPC 425 | then sStyle = 'rpc' 426 | else sStyle = 'document' 427 | return sStyle 428 | 429 | getUse: procedure expose g. 430 | if g.0OPTION.ENCODED 431 | then sUse = 'encoded' 432 | else sUse = 'literal' 433 | return sUse 434 | 435 | /* 436 | 437 | 439 | 441 | 442 | 443 | */ 444 | defineService: procedure expose g. 445 | parse arg node 446 | sService = getAttribute(node,'name') 447 | call setAttribute g.0SERVICE,'name',sService'Service' 448 | port = createElement('wsdl:port') 449 | call appendChild port,g.0SERVICE 450 | call setAttributes port,, 451 | 'binding','impl:'sService'SoapBinding',, 452 | 'name',sService 453 | addr = createElement('wsdlsoap:address') 454 | call appendChild addr,port 455 | call setAttribute addr,, 456 | 'location',g.0URL || sService 457 | return 458 | 459 | /* 460 | style=document, use=literal [WS-I compliant, with restrictions] 461 | 462 | Elements of the SOAP body are the names of XML Schema elements that 463 | describe each parameter (there is no wrapper operation and no multi-ref) 464 | 465 | 466 | 5 467 | 5.0 468 | 469 | 470 | */ 471 | createDocLiteral: procedure expose g. 472 | parse arg node 473 | say 'IDL003I Generating WSDL style=DOCUMENT use=LITERAL' 474 | /* 475 | 476 | 477 | 478 | 479 | 480 | 481 | 483 | 484 | . 485 | . 486 | */ 487 | programs = getChildrenByName(node,'programs') 488 | if programs <> '' 489 | then do 490 | programs = getChildren(programs) 491 | do i = 1 to words(programs) 492 | program = word(programs,i) 493 | sOperation = getAttribute(program,'name') 494 | request = createElement('wsdl:message') 495 | call appendChild request,g.0DEFS 496 | call setAttribute request,'name',sOperation'Request' 497 | response = createElement('wsdl:message') 498 | call appendChild response,g.0DEFS 499 | call setAttribute response,'name',sOperation'Response' 500 | parms = getChildren(program) 501 | do j = 1 to words(parms) 502 | parm = word(parms,j) 503 | sType = getAttribute(parm,'type') 504 | if sType <> '' /* if it is not a group */ 505 | then do 506 | sName = getAttribute(parm,'name') 507 | sDir = getAttribute(parm,'direction') 508 | if wordpos('In',sDir) > 0 509 | then call appendPartSchema sName,sType,request 510 | if wordpos('Out',sDir) > 0 511 | then call appendPartSchema sOperation'Return',sType,response 512 | end 513 | end 514 | end 515 | end 516 | return 517 | 518 | /* 519 | 520 | | 521 | .----------------------' 522 | | 523 | V 524 | 525 | or 526 | 527 | */ 528 | appendPartSchema: procedure expose g. 529 | parse arg sName,sEntireXType,node 530 | sElementName = sName 531 | if g.0USED.sElementName = 1 /* If this name is already used */ 532 | then do 533 | do i = 1 by 1 until g.0USED.sNameX = '' 534 | sNameX = sElementName || i 535 | end 536 | sElementName = sNameX 537 | end 538 | g.0USED.sElementName = 1 539 | element = createElement('element') 540 | call appendChild element,g.0SCHEMA 541 | call setAttributes element,, 542 | 'name',sElementName,, 543 | 'type',getSchemaEncoding(sEntireXType) 544 | part = createElement('wsdl:part') 545 | call appendChild part,node 546 | call setAttributes part,, 547 | 'name',sName,, 548 | 'element','impl:'sElementName 549 | return 550 | 551 | 552 | /* 553 | style=wrapped 554 | 555 | Special case of DOCLIT where there is only one parameter and it has the 556 | same qname as the operation. In such cases, there is no actual type with 557 | the name. The elements are treated as parameters to the operation 558 | 559 | 560 | 561 | 5 562 | 5.0 563 | 564 | 565 | 566 | */ 567 | createDocWrapped: procedure expose g. 568 | parse arg node 569 | say 'IDL003I Generating WSDL style=DOCUMENT (WRAPPED) use=LITERAL' 570 | /* 571 | 572 | 573 | 574 | 575 | 576 | 577 | . 578 | . 579 | */ 580 | programs = getChildrenByName(node,'programs') 581 | if programs <> '' 582 | then do 583 | programs = getChildren(programs) 584 | do i = 1 to words(programs) 585 | program = word(programs,i) 586 | sOperation = getAttribute(program,'name') 587 | sRequestElement = sOperation 588 | sResponseElement = sOperation'Response' 589 | 590 | call appendMessage sOperation'Request',sRequestElement 591 | call appendMessage sOperation'Response',sResponseElement 592 | 593 | request = getSequence(sRequestElement) 594 | response = getSequence(sResponseElement) 595 | parms = getChildren(program) 596 | do j = 1 to words(parms) 597 | parm = word(parms,j) 598 | sType = getAttribute(parm,'type') 599 | if sType <> '' /* if it is not a group */ 600 | then do 601 | sName = getAttribute(parm,'name') 602 | sDir = getAttribute(parm,'direction') 603 | if wordpos('In',sDir) > 0 604 | then call appendWrapped sName,sType,request 605 | if wordpos('Out',sDir) > 0 606 | then call appendWrapped sOperation'Return',sType,response 607 | end 608 | end 609 | end 610 | end 611 | return 612 | 613 | appendMessage: procedure expose g. 614 | parse arg sMessageName,sElementName 615 | message = createElement('wsdl:message') 616 | call appendChild message,g.0DEFS 617 | call setAttribute message,'name',sMessageName 618 | part = createElement('wsdl:part') 619 | call appendChild part,message 620 | call setAttributes part,, 621 | 'name','parameters',, 622 | 'element','impl:'sElementName 623 | return 624 | 625 | /* 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 639 | 640 | 641 | 642 | */ 643 | getSequence: procedure expose g. 644 | parse arg sName 645 | element = createElement('element') 646 | call appendChild element,g.0SCHEMA 647 | call setAttribute element,'name',sName 648 | complexType = createElement('complexType') 649 | call appendChild complexType,element 650 | sequence = createElement('sequence') 651 | call appendChild sequence,complexType 652 | return sequence 653 | 654 | /* 655 | 656 | or 657 | 658 | */ 659 | appendWrapped: procedure expose g. 660 | parse arg sName,sEntireXType,sequence 661 | element = createElement('element') 662 | call appendChild element,sequence 663 | call setAttributes element,, 664 | 'name',sName,, 665 | 'type',getSchemaEncoding(sEntireXType) 666 | return 667 | 668 | /* 669 | style=document, use=encoded [NOT WS-I compliant] 670 | 671 | There is no enclosing operation name element, but the parmeters are 672 | encoded using SOAP encoding. This mode is not (well?) supported by 673 | Apache Axis. 674 | 675 | */ 676 | createDocEncoded: procedure expose g. 677 | parse arg node 678 | say 'IDL099W WSDL style=DOCUMENT use=ENCODED not supported' 679 | return 680 | 681 | /* 682 | style=rpc, use=literal 683 | 684 | First element of the SOAP body is the operation. 685 | The operation contains elements describing the parameters, 686 | which are not serialized as encoded (and no multi-ref) 687 | 688 | 689 | 690 | 5 691 | 5.0 692 | 693 | 694 | 695 | */ 696 | createRpcLiteral: procedure expose g. 697 | parse arg node 698 | say 'IDL003I Generating WSDL style=RPC use=LITERAL' 699 | /* 700 | 701 | 702 | 703 | 704 | 705 | 706 | 708 | 709 | . 710 | . 711 | */ 712 | programs = getChildrenByName(node,'programs') 713 | if programs <> '' 714 | then do 715 | programs = getChildren(programs) 716 | do i = 1 to words(programs) 717 | program = word(programs,i) 718 | sOperation = getAttribute(program,'name') 719 | request = createElement('wsdl:message') 720 | call appendChild request,g.0DEFS 721 | call setAttribute request,'name',sOperation'Request' 722 | response = createElement('wsdl:message') 723 | call appendChild response,g.0DEFS 724 | call setAttribute response,'name',sOperation'Response' 725 | parms = getChildren(program) 726 | do j = 1 to words(parms) 727 | parm = word(parms,j) 728 | sType = getAttribute(parm,'type') 729 | if sType <> '' /* if it is not a group */ 730 | then do 731 | sName = getAttribute(parm,'name') 732 | sDir = getAttribute(parm,'direction') 733 | if wordpos('In',sDir) > 0 734 | then call appendPartType sName,sType,request 735 | if wordpos('Out',sDir) > 0 736 | then call appendPartType sOperation'Return',sType,response 737 | end 738 | end 739 | end 740 | end 741 | return 742 | 743 | /* 744 | style=rpc, use=encoded [NOT WS-I compliant] 745 | 746 | First element of the SOAP body is the operation. 747 | The operation contains elements describing the parameters, 748 | which are serialized as encoded (possibly multi-ref) 749 | 750 | 751 | 752 | 5 753 | 5.0 754 | 755 | 756 | 757 | */ 758 | createRpcEncoded: procedure expose g. 759 | parse arg node 760 | say 'IDL003I Generating WSDL style=RPC use=ENCODED' 761 | /* 762 | 763 | 764 | 765 | 766 | 767 | 768 | 770 | 771 | . 772 | . 773 | */ 774 | programs = getChildrenByName(node,'programs') 775 | if programs <> '' 776 | then do 777 | programs = getChildren(programs) 778 | do i = 1 to words(programs) 779 | program = word(programs,i) 780 | sOperation = getAttribute(program,'name') 781 | request = createElement('wsdl:message') 782 | call appendChild request,g.0DEFS 783 | call setAttribute request,'name',sOperation'Request' 784 | response = createElement('wsdl:message') 785 | call appendChild response,g.0DEFS 786 | call setAttribute response,'name',sOperation'Response' 787 | parms = getChildren(program) 788 | do j = 1 to words(parms) 789 | parm = word(parms,j) 790 | sType = getAttribute(parm,'type') 791 | if sType <> '' /* if it is not a group */ 792 | then do 793 | sName = getAttribute(parm,'name') 794 | sDir = getAttribute(parm,'direction') 795 | if wordpos('In',sDir) > 0 796 | then call appendPartType sName,sType,request 797 | if wordpos('Out',sDir) > 0 798 | then call appendPartType sOperation'Return',sType,response 799 | end 800 | end 801 | end 802 | end 803 | return 804 | 805 | /* 806 | 807 | or 808 | 809 | */ 810 | appendPartType: procedure expose g. 811 | parse arg sName,sEntireXType,node 812 | part = createElement('wsdl:part') 813 | call appendChild part,node 814 | call setAttributes part,, 815 | 'name',sName,, 816 | 'type',getEncoding(sEntireXType) 817 | return 818 | 819 | /* 820 | 821 | 822 | 824 | 826 | 828 | 830 | 832 | 833 | 834 | */ 835 | appendComplexType: procedure expose g. 836 | parse arg struct,schema 837 | sStructureName = getAttribute(struct,'name') 838 | complexType = createElement('complexType') 839 | call appendChild complexType,schema 840 | call setAttribute complexType,'name',sStructureName 841 | sequence = createElement('sequence') 842 | call appendChild sequence,complexType 843 | parms = getChildNodes(struct) 844 | do i = 1 to words(parms) 845 | parm = word(parms,i) 846 | nLevel = getAttribute(parm,'level') 847 | sName = getAttribute(parm,'name') 848 | sType = getAttribute(parm,'type') 849 | sDirection = getAttribute(parm,'direction') 850 | select 851 | when sType = '' then do 852 | /* ignore an EntireX grouping level */ 853 | end 854 | when left(sType,1) = "'" then do 855 | parse var sType "'"sRef"'" 856 | end 857 | otherwise do 858 | call appendElement sName,sType,sequence 859 | end 860 | end 861 | end 862 | return 863 | 864 | /* 865 | 866 | 868 | 870 | 872 | 874 | 876 | 877 | */ 878 | appendElement: procedure expose g. 879 | parse arg sName,sEntireXType,sequence 880 | element = createElement('element') 881 | call appendChild element,sequence 882 | call setAttributes element,, 883 | 'name',sName,, 884 | 'nillable','true',, 885 | 'type',getEncoding(sEntireXType) 886 | return 887 | 888 | getEncoding: procedure expose g. 889 | parse arg sEntireXType 890 | if g.0OPTION.ENCODED 891 | then sEncoding = getSoapEncoding(sEntireXType) 892 | else sEncoding = getSchemaEncoding(sEntireXType) 893 | return sEncoding 894 | 895 | /* Map an EntireX data type to a SOAP encoded type */ 896 | getSoapEncoding: procedure expose g. 897 | parse arg sEntireXType . 1 sType1 +1 1 sType2 +2 898 | select 899 | when sType1 = 'A' then do /* alphanumeric */ 900 | if sType2 = 'AV' /* variable length */ 901 | then sEncoding = 'soapenc:string' 902 | else sEncoding = 'soapenc:string' 903 | end 904 | when sType1 = 'B' then do /* binary */ 905 | if sType2 = 'BV' /* variable length */ 906 | then sEncoding = 'soapenc:int' 907 | else sEncoding = 'soapenc:int' 908 | end 909 | when sType1 = 'D' then do /* date */ 910 | sEncoding = 'soapenc:date' 911 | end 912 | when sType1 = 'F' then do /* floating point */ 913 | sEncoding = 'soapenc:float' 914 | end 915 | when sType1 = 'I' then do /* integer */ 916 | sEncoding = 'soapenc:int' 917 | end 918 | when sType1 = 'L' then do /* logical */ 919 | sEncoding = 'soapenc:boolean' 920 | end 921 | when sType1 = 'N' then do /* numeric */ 922 | if sType2 = 'NU' /* unsigned */ 923 | then sEncoding = 'soapenc:decimal' 924 | else sEncoding = 'soapenc:decimal' 925 | end 926 | when sType1 = 'P' then do /* packed decimal */ 927 | if sType2 = 'PU' /* unsigned */ 928 | then sEncoding = 'soapenc:decimal' 929 | else sEncoding = 'soapenc:decimal' 930 | end 931 | when sType1 = 'T' then do /* time */ 932 | sEncoding = 'soapenc:time' 933 | end 934 | when sType1 = "'" then do /* reference to a struct */ 935 | parse var sEntireXType "'"sReference"'" 936 | sEncoding = 'impl:'sReference 937 | end 938 | otherwise do 939 | sEncoding = 'soapenc:anyType' 940 | end 941 | end 942 | return sEncoding 943 | 944 | /* Map an EntireX data type to an XML schema data type */ 945 | getSchemaEncoding: procedure expose g. 946 | parse arg sEntireXType . 1 sType1 +1 1 sType2 +2 947 | select 948 | when sType1 = 'A' then do /* alphanumeric */ 949 | if sType2 = 'AV' /* variable length */ 950 | then sEncoding = 'xsd:string' 951 | else sEncoding = 'xsd:string' 952 | end 953 | when sType1 = 'B' then do /* binary */ 954 | if sType2 = 'BV' /* variable length */ 955 | then sEncoding = 'xsd:int' 956 | else sEncoding = 'xsd:int' 957 | end 958 | when sType1 = 'D' then do /* date */ 959 | sEncoding = 'xsd:date' 960 | end 961 | when sType1 = 'F' then do /* floating point */ 962 | sEncoding = 'xsd:float' 963 | end 964 | when sType1 = 'I' then do /* integer */ 965 | sEncoding = 'xsd:int' 966 | end 967 | when sType1 = 'L' then do /* logical */ 968 | sEncoding = 'xsd:boolean' 969 | end 970 | when sType1 = 'N' then do /* numeric */ 971 | if sType2 = 'NU' /* unsigned */ 972 | then sEncoding = 'xsd:decimal' 973 | else sEncoding = 'xsd:decimal' 974 | end 975 | when sType1 = 'P' then do /* packed decimal */ 976 | if sType2 = 'PU' /* unsigned */ 977 | then sEncoding = 'xsd:decimal' 978 | else sEncoding = 'xsd:decimal' 979 | end 980 | when sType1 = 'T' then do /* time */ 981 | sEncoding = 'xsd:time' 982 | end 983 | when sType1 = "'" then do /* reference to a struct */ 984 | parse var sEntireXType "'"sReference"'" 985 | sEncoding = 'impl:'sReference 986 | end 987 | otherwise do 988 | sEncoding = 'xsd:anyType' 989 | end 990 | end 991 | return sEncoding 992 | 993 | 994 | getFilenameWithoutExtension: procedure expose g. 995 | parse arg sFile 996 | parse value reverse(sFile) with '.'sRest 997 | return reverse(sRest) 998 | 999 | scanEntireXIdlFile: procedure expose g. 1000 | idl = createElement('idl') 1001 | g.0FILEIN = openFile(g.0FILEIDL) 1002 | sLine = getNextLine() 1003 | do while g.0RC = 0 & sLine <> '** End of file' 1004 | parse var sLine sAction sName ' is' 1005 | select 1006 | when sAction = 'library' then do 1007 | parse var sName sName':'sAlias 1008 | sName = strip(sName,'BOTH',"'") 1009 | sAlias = strip(sAlias,'BOTH',"'") 1010 | library = createElement('library') 1011 | call setAttributes library,, 1012 | 'name',sName,, 1013 | 'alias',sAlias 1014 | call appendChild library,idl 1015 | structures = createElement('structures') 1016 | call appendChild structures,library 1017 | programs = createElement('programs') 1018 | call appendChild programs,library 1019 | end 1020 | when sAction = 'program' then do 1021 | parse var sName sName':'sAlias 1022 | sName = strip(sName,'BOTH',"'") 1023 | sAlias = strip(sAlias,'BOTH',"'") 1024 | program = createElement('program') 1025 | call setAttributes program,, 1026 | 'name',sName,, 1027 | 'alias',sAlias 1028 | call appendChild program,programs 1029 | call getParameters program 1030 | end 1031 | when sAction = 'struct' then do 1032 | parse var sName sName':'sAlias 1033 | g.0STRUCT = sName 1034 | sName = strip(sName,'BOTH',"'") 1035 | sAlias = strip(sAlias,'BOTH',"'") 1036 | struct = createElement('struct') 1037 | call setAttributes struct,, 1038 | 'name',sName,, 1039 | 'alias',sAlias 1040 | call appendChild struct,structures 1041 | call getParameters struct 1042 | end 1043 | otherwise do 1044 | say 'IDL002E Unknown IDL file input line:' sLine 1045 | end 1046 | end 1047 | sLine = getNextLine() 1048 | end 1049 | rc = closeFile(g.0FILEIN) 1050 | return idl 1051 | 1052 | getParameters: procedure expose g. 1053 | parse arg parent 1054 | sLine = getLineContaining('define data parameter') 1055 | if g.0RC <> 0 then return 1056 | sLine = getNextLine() 1057 | do while g.0RC = 0 & sLine <> 'end-define' 1058 | parse var sLine nLevel sName '('sType')' sDirection 1059 | parm = createElement('parm') 1060 | call appendChild parm,parent 1061 | call setAttributes parm,, 1062 | 'level',nLevel,, 1063 | 'name',strip(sName),, 1064 | 'type',sType 1065 | if sDirection <> '' 1066 | then call setAttribute parm,'direction',strip(sDirection) 1067 | sLine = getNextLine() 1068 | end 1069 | return 1070 | 1071 | getLineContaining: procedure expose g. 1072 | parse arg sSearchArg 1073 | sLine = getLine(g.0FILEIN) 1074 | do while g.0RC = 0 & pos(sSearchArg, sLine) = 0 1075 | sLine = getLine(g.0FILEIN) 1076 | end 1077 | return sLine 1078 | 1079 | getNextLine: procedure expose g. 1080 | sLine = removeWhiteSpace(getLine(g.0FILEIN)) 1081 | do while g.0RC = 0 & (sLine = '' | left(sLine,2) = '/*') 1082 | sLine = removeWhiteSpace(getLine(g.0FILEIN)) 1083 | end 1084 | if pos('/*',sLine) > 0 1085 | then parse var sLine sLine '/*' . 1086 | return sLine 1087 | 1088 | setOptions: procedure expose g. 1089 | parse upper arg sOptions 1090 | /* set default options... */ 1091 | g.0OPTION.DUMP = 0 1092 | g.0OPTION.XML = 0 1093 | g.0OPTION.RPC = 0 1094 | g.0OPTION.DOCUMENT = 0 1095 | g.0OPTION.ENCODED = 0 1096 | g.0OPTION.LITERAL = 0 1097 | g.0OPTION.WRAPPED = 0 1098 | do i = 1 to words(sOptions) 1099 | sOption = word(sOptions,i) 1100 | g.0OPTION.sOption = 1 1101 | end 1102 | if g.0OPTION.RPC | g.0OPTION.DOCUMENT |, 1103 | g.0OPTION.ENCODED | g.0OPTION.LITERAL | g.0OPTION.WRAPPED 1104 | then nop 1105 | else do /* Set the default style... */ 1106 | g.0OPTION.WRAPPED = 1 1107 | end 1108 | if g.0OPTION.WRAPPED 1109 | then do 1110 | g.0OPTION.DOCUMENT = 1 1111 | g.0OPTION.LITERAL = 1 1112 | g.0OPTION.RPC = 0 1113 | g.0OPTION.ENCODED = 0 1114 | end 1115 | if g.0OPTION.DOCUMENT then g.0OPTION.RPC = 0 1116 | if g.0OPTION.RPC then g.0OPTION.DOCUMENT = 0 1117 | if g.0OPTION.LITERAL then g.0OPTION.ENCODED = 0 1118 | if g.0OPTION.ENCODED then g.0OPTION.LITERAL = 0 1119 | return 1120 | 1121 | Prolog: 1122 | if g.0ENV = 'TSO' 1123 | then g.0LF = '15'x 1124 | else g.0LF = '0A'x 1125 | doc = createDocument('dummy') /* just to get structures in place */ 1126 | return 1127 | 1128 | Epilog: procedure expose g. 1129 | return 1130 | 1131 | /*INCLUDE pretty.rex */ 1132 | -------------------------------------------------------------------------------- /idlfile.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | /** This is an example IDL file that you can process using the 3 | /** IDL2WSDL rexx procedure. 4 | /********************************************************************* 5 | library 'TalkingClock' is 6 | program 'getTime' is 7 | define data parameter 8 | 1 result (A32) Out 9 | end-define 10 | 11 | program 'Sprechen' is 12 | define data parameter 13 | 1 result (AV80) Out 14 | end-define 15 | 16 | program 'Speak' is 17 | define data parameter 18 | 1 result (AV80) Out 19 | end-define 20 | 21 | library 'Security' is 22 | program 'changePassword' is 23 | define data parameter 24 | 1 userid (A8) In 25 | 1 password (A8) In 26 | 1 newPassword (A8) In 27 | 1 result ('SecurityContext') Out 28 | end-define 29 | 30 | program 'verify' is /*Authenticate a userid 31 | define data parameter 32 | 1 userid (A8) In 33 | 1 password (A8) In 34 | 1 result ('SecurityContext') Out 35 | end-define 36 | 37 | program 'authorise' is /*Authorise access to a resource 38 | define data parameter 39 | 1 userid (A8) In 40 | 1 password (A8) In 41 | 1 resource (A44) In 42 | 1 result ('SecurityContext') Out 43 | end-define 44 | 45 | /*The result of a Security operation is a collection of return and reason codes 46 | struct 'SecurityContext' is 47 | define data parameter 48 | 1 userid (A8) 49 | 1 success (L) 50 | 1 returnCode (B4) 51 | 1 reasonCode (B4) 52 | 1 reason (AV80) 53 | end-define 54 | 55 | /*Preceding comment lines 56 | library 'Gamut':'Gamut Alias' is 57 | /*Same line comment 58 | struct 'aStructure' is 59 | define data parameter 60 | 1 aVar2 (D) 61 | 1 aVar3 (A1) 62 | 1 aVar4 (BV) 63 | 1 aVar5 (F4) 64 | 1 aVar6 (I4) 65 | 1 aVar7 (L) 66 | 1 aVar8 (N8.3) 67 | 1 aVar9 (NU8.3) 68 | 1 aVar10 (P8.3) 69 | 1 aVar11 (PU8.3) 70 | 1 aVar12 (T) 71 | 1 aGroup 72 | 2 aVar1 (AV80/1,2,3) aligned 73 | 2 aGroup2 74 | 3 aVar3 (AV) 75 | 3 aVar2 (AV) 76 | end-define 77 | 78 | /*Preceding comment lines 79 | program 'Operation':'Operation Alias' is /*Same line comment 80 | define data parameter 81 | 1 aParm1 (A1) In 82 | 1 aParm2 (AV) Out 83 | 1 aStructureRef ('aStructure') In 84 | 1 aStructureRef2 ('aStructure') Out 85 | 1 aStructureRef3 ('aStructure') In Out 86 | end-define 87 | 88 | 89 | ** End of file 90 | -------------------------------------------------------------------------------- /images/lose-the-horse-luke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abend0c1/rexxxmlparser/488d40d94449e049d94a4eef2416ec46011efda4/images/lose-the-horse-luke.jpg -------------------------------------------------------------------------------- /io.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - IO ** 33 | ** ** 34 | ** FUNCTION - Simple I/O routines. ** 35 | ** ** 36 | ** API - The routines in this module are: ** 37 | ** ** 38 | ** openFile(filename,options,attrs) ** 39 | ** Opens the specified file with the specified options** 40 | ** and returns a file handle to be used in other I/O ** 41 | ** operations. By default the file will be opened for ** 42 | ** input. Specify 'OUTPUT' to open it for output. ** 43 | ** For TSO, you can specify any operand of the TSO ** 44 | ** ALLOCATE command in the third operand. For example:** 45 | ** rc = openFile('MY.FILE','OUTPUT','RECFM(F,B)' ** 46 | ** 'LRECL(80) BLKSIZE(27920)') ** 47 | ** ** 48 | ** closeFile(handle) ** 49 | ** Closes the file specified by 'handle' (which was ** 50 | ** returned by the openFile() routine. ** 51 | ** ** 52 | ** getLine(handle) ** 53 | ** Reads the next line from the file specified by ** 54 | ** 'handle'. ** 55 | ** ** 56 | ** putLine(handle,data) ** 57 | ** Appends the specified data to the file specified ** 58 | ** by 'handle'. ** 59 | ** ** 60 | ** ** 61 | ** AUTHOR - Andrew J. Armstrong ** 62 | ** ** 63 | ** HISTORY - Date By Reason (most recent at the top please) ** 64 | ** -------- --------------------------------------------- ** 65 | ** 20090822 AJA Changed from GPL to BSD license. ** 66 | ** 20061017 AJA Added support for UNIX environment. ** 67 | ** Tested on Ubuntu Linux 6.06 LTS. ** 68 | ** 20050930 AJA Initial version. ** 69 | ** ** 70 | **********************************************************************/ 71 | 72 | parse source . . sSourceFile . 73 | parse value sourceline(1) with . sVersion 74 | say 'Simple Rexx I/O routines' sVersion 75 | say 'You cannot invoke this rexx by itself!' 76 | say 77 | say 'This rexx is a collection of subroutines to be called' 78 | say 'from your own rexx procedures. You should either:' 79 | say ' - Append this procedure to your own rexx procedure,' 80 | say ' or,' 81 | say ' - Append the following line to your rexx:' 82 | say ' /* INCLUDE' sSourceFile '*/' 83 | say ' ...and run the rexx preprocessor:' 84 | say ' rexxpp myrexx myrexxpp' 85 | say ' This will create myrexxpp by appending this file to myrexx' 86 | exit 87 | 88 | /*-------------------------------------------------------------------* 89 | * Open a file 90 | *-------------------------------------------------------------------*/ 91 | 92 | openFile: procedure expose g. 93 | parse arg sFile,sOptions,sAttrs 94 | hFile = '' 95 | select 96 | when g.0ENV = 'TSO' then do 97 | bOutput = wordpos('OUTPUT',sOptions) > 0 98 | bQuoted = left(sFile,1) = "'" 99 | if bQuoted then sFile = strip(sFile,,"'") 100 | parse var sFile sDataset'('sMember')' 101 | if sMember <> '' then sFile = sDataset 102 | if bQuoted then sFile = "'"sFile"'" 103 | if bOutput 104 | then 'LMINIT DATAID(hFile) DATASET(&sFile) ENQ(EXCLU)' 105 | else 'LMINIT DATAID(hFile) DATASET(&sFile)' 106 | if sMember <> '' 107 | then do /* Open a member of a PDS */ 108 | 'LMOPEN DATAID(&hFile) OPTION(INPUT)' /* Input initially */ 109 | /* ... can't update ISPF stats when opened for output */ 110 | g.0MEMBER.hFile = sMember 111 | 'LMMFIND DATAID(&hFile) MEMBER('sMember') STATS(YES)' 112 | if bOutput 113 | then do 114 | if rc = 0 115 | then g.0STATS.hFile = zlvers','zlmod','zlc4date 116 | else g.0STATS.hFile = '1,0,0000/00/00' 117 | 'LMCLOSE DATAID(&hFile)' 118 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 119 | end 120 | end 121 | else do /* Open a sequential dataset */ 122 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 123 | if rc <> 0 /* If dataset does not already exist... */ 124 | then do /* Create sequential dataset then open it */ 125 | 'LMCLOSE DATAID(&hFile)' 126 | 'LMFREE DATAID(&hFile)' 127 | address TSO 'ALLOCATE DATASET('sFile') NEW CATALOG', 128 | 'SPACE(5,15) TRACKS RECFM(V,B)', 129 | 'LRECL('g.0OPTION.WRAP.1 + 4')', 130 | 'BLKSIZE(27990)' sAttrs 131 | if bOutput 132 | then do 133 | 'LMINIT DATAID(hFile) DATASET(&sFile) ENQ(EXCLU)' 134 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 135 | end 136 | else do 137 | 'LMINIT DATAID(hFile) DATASET(&sFile)' 138 | 'LMOPEN DATAID(&hFile) OPTION(INPUT)' 139 | end 140 | end 141 | end 142 | g.0OPTIONS.hFile = sOptions 143 | g.0rc = rc /* Return code from LMOPEN */ 144 | end 145 | otherwise do 146 | if wordpos('OUTPUT',sOptions) > 0 147 | then junk = stream(sFile,'COMMAND','OPEN WRITE REPLACE') 148 | else junk = stream(sFile,'COMMAND','OPEN READ') 149 | hFile = sFile 150 | if stream(sFile,'STATUS') = 'READY' 151 | then g.0rc = 0 152 | else g.0rc = 4 153 | end 154 | end 155 | return hFile 156 | 157 | /*-------------------------------------------------------------------* 158 | * Read a line from the specified file 159 | *-------------------------------------------------------------------*/ 160 | 161 | getLine: procedure expose g. 162 | parse arg hFile 163 | sLine = '' 164 | select 165 | when g.0ENV = 'TSO' then do 166 | 'LMGET DATAID(&hFile) MODE(INVAR)', 167 | 'DATALOC(sLine) DATALEN(nLine) MAXLEN(32768)' 168 | g.0rc = rc 169 | sLine = strip(sLine,'TRAILING') 170 | if sLine = '' then sLine = ' ' 171 | end 172 | otherwise do 173 | g.0rc = 0 174 | if chars(hFile) > 0 175 | then sLine = linein(hFile) 176 | else g.0rc = 4 177 | end 178 | end 179 | return sLine 180 | 181 | /*-------------------------------------------------------------------* 182 | * Append a line to the specified file 183 | *-------------------------------------------------------------------*/ 184 | 185 | putLine: procedure expose g. 186 | parse arg hFile,sLine 187 | select 188 | when g.0ENV = 'TSO' then do 189 | g.0LINES = g.0LINES + 1 190 | 'LMPUT DATAID(&hFile) MODE(INVAR)', 191 | 'DATALOC(sLine) DATALEN('length(sLine)')' 192 | end 193 | otherwise do 194 | junk = lineout(hFile,sLine) 195 | rc = 0 196 | end 197 | end 198 | return rc 199 | 200 | /*-------------------------------------------------------------------* 201 | * Close the specified file 202 | *-------------------------------------------------------------------*/ 203 | 204 | closeFile: procedure expose g. 205 | parse arg hFile 206 | rc = 0 207 | select 208 | when g.0ENV = 'TSO' then do 209 | if g.0MEMBER.hFile <> '', /* if its a PDS */ 210 | & wordpos('OUTPUT',g.0OPTIONS.hFile) > 0 /* opened for output */ 211 | then do 212 | parse value date('STANDARD') with yyyy +4 mm +2 dd +2 213 | parse var g.0STATS.hFile zlvers','zlmod','zlc4date 214 | zlcnorc = min(g.0LINES,65535) /* Number of lines */ 215 | nVer = right(zlvers,2,'0')right(zlmod,2,'0') /* vvmm */ 216 | nVer = right(nVer+1,4,'0') /* vvmm + 1 */ 217 | parse var nVer zlvers +2 zlmod +2 218 | if zlc4date = '0000/00/00' 219 | then zlc4date = yyyy'/'mm'/'dd /* Creation date */ 220 | zlm4date = yyyy'/'mm'/'dd /* Modification date */ 221 | zlmtime = time() /* Modification time */ 222 | zluser = userid() /* Modification user */ 223 | 'LMMREP DATAID(&hFile) MEMBER('g.0MEMBER.hFile') STATS(YES)' 224 | end 225 | 'LMCLOSE DATAID(&hFile)' 226 | 'LMFREE DATAID(&hFile)' 227 | end 228 | otherwise do 229 | if stream(hFile,'COMMAND','CLOSE') = 'UNKNOWN' 230 | then rc = 0 231 | else rc = 4 232 | end 233 | end 234 | return rc -------------------------------------------------------------------------------- /pretty.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - PRETTY ** 33 | ** ** 34 | ** FUNCTION - Pretty printer. This demonstrates the XML parser by ** 35 | ** reformatting an xml input file. ** 36 | ** ** 37 | ** ** 38 | ** SYNTAX - pretty infile [outfile] (options...) ** 39 | ** ** 40 | ** Where, ** 41 | ** infile = Name of file to be parsed ** 42 | ** outfile = Name of file to store the pretty output in. ** 43 | ** The default is the console. ** 44 | ** options = NOBLANKS - Suppress whitespace-only nodes ** 45 | ** DEBUG - Display some debugging info ** 46 | ** DUMP - Display the parse tree ** 47 | ** ** 48 | ** ** 49 | ** NOTES - 1. You will have to either append the PARSEXML source ** 50 | ** manually to this demo source, or run this demo ** 51 | ** source through the REXXPP rexx pre-processor. ** 52 | ** ** 53 | ** To use the pre-processor, run: ** 54 | ** ** 55 | ** rexxpp pretty prettypp ** 56 | ** ** 57 | ** ...and then run the resulting rexx procedure over ** 58 | ** an XML file of your choice: ** 59 | ** ** 60 | ** prettypp testxml [outxml] ** 61 | ** ...or... ** 62 | ** prettypp testxml [outxml] (noblanks ** 63 | ** ...or... ** 64 | ** prettypp testxml [outxml] (noblanks dump ** 65 | ** ** 66 | ** ** 67 | ** ** 68 | ** AUTHOR - Andrew J. Armstrong '' 119 | then do 120 | g.0FILEOUT = openFile(sFileOut,'OUTPUT') 121 | if g.0rc = 0 122 | then say 'PRP001I Creating' sFileOut 123 | else do 124 | say 'PRP002E Could not create' sFileOut'. Writing to console...' 125 | g.0FILEOUT = '' /* null handle means write to console */ 126 | end 127 | end 128 | 129 | call _setDefaultEntities 130 | 131 | call emitProlog 132 | if g.0NONRECURSIVE = 1 133 | then do 134 | call showNodeNonRecursive nRoot 135 | end 136 | else do 137 | g.0INDENT = -g.0TAB 138 | call showNode nRoot 139 | end 140 | 141 | if g.0FILEOUT <> '' 142 | then do 143 | say 'PRP002I Created' sFileOut 144 | rc = closeFile(g.0FILEOUT) 145 | end 146 | return 147 | 148 | showNodeNonRecursive: procedure expose g. 149 | parse arg topNodeAgain 150 | g.0INDENT = 0 151 | node = topNodeAgain 152 | do until node = topNodeAgain | node = 0 153 | if g.0SEEN.node = '' 154 | then call emitBegin node 155 | else call emitEnd node 156 | g.0SEEN.node = 1 157 | if g.0SOUTH.node = '' then do 158 | g.0SOUTH.node = 1 159 | if hasChildren(node) 160 | then do 161 | node = getFirstChild(node) 162 | end 163 | else do 164 | call emitEnd node 165 | nextSibling = getNextSibling(node) 166 | if nextSibling = '' 167 | then node = getParent(node) 168 | else node = nextSibling 169 | end 170 | end 171 | else do 172 | nextSibling = getNextSibling(node) 173 | if nextSibling = '' 174 | then node = getParent(node) 175 | else node = nextSibling 176 | end 177 | end 178 | call emitEnd node 179 | drop g.0SOUTH. g.0SEEN. 180 | return 181 | 182 | emitBegin: procedure expose g. 183 | parse arg node 184 | select 185 | when isElementNode(node) then call emitElementNodeNonRecursive(node) 186 | when isTextNode(node) then call emitTextNode(node) 187 | when isCommentNode(node) then call emitCommentNode(node) 188 | when isCDATA(node) then call emitCDATA(node) 189 | otherwise nop 190 | end 191 | g.0INDENT = g.0INDENT + 2 192 | return 193 | 194 | emitEnd: procedure expose g. 195 | parse arg node 196 | g.0INDENT = g.0INDENT - 2 197 | if isElementNode(node) & hasChildren(node) 198 | then call Say '' 199 | return 200 | 201 | emitElementNodeNonRecursive: procedure expose g. 202 | parse arg node 203 | if hasChildren(node) 204 | then call Say '<'getName(node)getAttrs(node)'>' 205 | else call Say '<'getName(node)getAttrs(node)'/>' 206 | return 207 | 208 | getAttrs: procedure expose g. 209 | parse arg node 210 | sAttrs = '' 211 | do i = 1 to getAttributeCount(node) 212 | sAttrs = sAttrs getAttributeName(node,i)'="' ||, 213 | escapeText(getAttribute(node,i))'"' 214 | end 215 | return sAttrs 216 | 217 | emitProlog: procedure expose g. 218 | if g.?xml.version = '' 219 | then sVersion = '1.0' 220 | else sVersion = g.?xml.version 221 | if g.?xml.encoding = '' 222 | then sEncoding = 'UTF-8' 223 | else sEncoding = g.?xml.encoding 224 | if g.?xml.standalone = '' 225 | then sStandalone = 'yes' 226 | else sStandalone = g.?xml.standalone 227 | 228 | g.0INDENT = 0 229 | call Say '' 232 | 233 | sDocType = getDocType() 234 | if sDocType <> '' 235 | then call Say '' 236 | return 237 | 238 | showNode: procedure expose g. 239 | parse arg node 240 | g.0INDENT = g.0INDENT + g.0TAB 241 | select 242 | when isTextNode(node) then call emitTextNode node 243 | when isCommentNode(node) then call emitCommentNode node 244 | when isCDATA(node) then call emitCDATA node 245 | otherwise call emitElementNode node 246 | end 247 | g.0INDENT = g.0INDENT - g.0TAB 248 | return 249 | 250 | setPreserveWhitespace: procedure expose g. 251 | parse arg bPreserve 252 | g.0PRESERVEWS = bPreserve = 1 253 | return 254 | 255 | emitTextNode: procedure expose g. 256 | parse arg node 257 | sText = getText(node) 258 | if sText = '' then return 259 | if g.0PRESERVEWS = 1 260 | then call Say escapeText(sText) 261 | else call Say escapeText(removeWhitespace(sText)) 262 | return 263 | 264 | emitCommentNode: procedure expose g. 265 | parse arg node 266 | call Say '' 267 | return 268 | 269 | emitCDATA: procedure expose g. 270 | parse arg node 271 | call Say '' 272 | return 273 | 274 | emitElementNode: procedure expose g. 275 | parse arg node 276 | sName = getName(node) 277 | sAttrs = '' 278 | do i = 1 to getAttributeCount(node) 279 | sAttrs = sAttrs getAttributeName(node,i)'="' ||, 280 | escapeText(getAttribute(node,i))'"' 281 | end 282 | sChildren = getChildren(node) 283 | if sChildren = '' 284 | then do 285 | if sAttrs = '' 286 | then call Say '<'sName'/>' 287 | else call Say '<'sName strip(sAttrs)'/>' 288 | end 289 | else do 290 | if sAttrs = '' 291 | then call Say '<'sName'>' 292 | else call Say '<'sName strip(sAttrs)'>' 293 | child = getFirstChild(node) 294 | do while child <> '' 295 | call showNode child 296 | child = getNextSibling(child) 297 | end 298 | call Say '' 299 | end 300 | return 301 | 302 | Say: procedure expose g. 303 | parse arg sMessage 304 | sLine = copies(' ',g.0INDENT)sMessage 305 | if g.0FILEOUT = '' 306 | then say sLine 307 | else call putLine g.0FILEOUT,sLine 308 | return 309 | 310 | /*INCLUDE io.rex */ 311 | /*INCLUDE parsexml.rex */ 312 | -------------------------------------------------------------------------------- /rexxpp.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - REXXPP ** 33 | ** ** 34 | ** FUNCTION - Rexx INCLUDE pre-processor. This rexx will read the ** 35 | ** specified filename looking for /*INCLUDE file */ ** 36 | ** directives. For each INCLUDE found, it will append ** 37 | ** the content of the specified file to the output file. ** 38 | ** ** 39 | ** The start and end of the included content is marked by ** 40 | ** /*INCLUDED file */ and /*INCLUDEZ file */ lines. Remove** 41 | ** these markers at your peril, because they allow REXXPP ** 42 | ** to intelligently handle a file that has already been ** 43 | ** pre-processed. ** 44 | ** ** 45 | ** ** 46 | ** SYNTAX - REXXPP filein fileout ** 47 | ** ** 48 | ** Where, ** 49 | ** filein = Name of the file to pre-process. ** 50 | ** fileout = Name of the file to contain the resulting ** 51 | ** output. On TSO, the default for fileout is ** 52 | ** filein. On Windows there is no default. ** 53 | ** ** 54 | ** EXAMPLE - 1. If file1 contains: ** 55 | ** say "it's goodnight from me..." ** 56 | ** /*INCLUDE file2 */ /* <-- Note: starts in column 1 */ ** 57 | ** exit ** 58 | ** ** 59 | ** 2. And file2 contains: ** 60 | ** say "...and it's goodnight from him" ** 61 | ** ** 62 | ** 3. And you run: rexxpp file1 file3 ** 63 | ** ** 64 | ** 4. Then file3 will contain: ** 65 | ** say "it's goodnight from me..." ** 66 | ** /*INCLUDED file2 */ ** 67 | ** say "...and it's goodnight from him" ** 68 | ** /*INCLUDEZ file2 */ ** 69 | ** exit ** 70 | ** ** 71 | ** AUTHOR - Andrew J. Armstrong ** 72 | ** ** 73 | ** HISTORY - Date By Reason (most recent at the top please) ** 74 | ** -------- --------------------------------------------- ** 75 | ** 20090822 AJA Changed from GPL to BSD license. ** 76 | ** 20060505 AJA Prompt user for missing file names. ** 77 | ** 20050601 AJA Ensure each file included only once. ** 78 | ** 20050518 AJA Default fileout is filein (TSO only). ** 79 | ** 20031217 AJA Allow output to be re-processed. ** 80 | ** 20030912 AJA Use stream() to close win32 file. ** 81 | ** 20030904 AJA Initial version. ** 82 | ** ** 83 | **********************************************************************/ 84 | 85 | parse arg sFileIn sFileOut' ('sOptions')' 86 | parse source g.0SYSTEM . 87 | if g.0SYSTEM = 'TSO' 88 | then do 89 | address ISPEXEC 90 | 'CONTROL ERRORS RETURN' 91 | g.0LINES = 0 92 | if sFileOut = '' 93 | then sFileOut = sFileIn /* you can do this on TSO but not Windows*/ 94 | end 95 | g.0LEVEL = 0 96 | parse value sourceline(1) with . sVersion 97 | call Say 'RPP000I Rexx INCLUDE Pre-processor' sVersion 98 | call Say 'RPP001I Output file is:' sFileOut 99 | sFileOut = getFile(sFileOut,'OUTPUT') 100 | hFileOut = g.0hFile 101 | if g.0rc = 0 102 | then do 103 | call Say 'RPP001I Input file is:' sFileIn 104 | sFileIn = getFile(sFileIn) 105 | hFileIn = g.0hFile 106 | call includeFile sFileIn,hFileIn,hFileOut 107 | end 108 | else call Say 'RPP003E Could not create file:' sFileOut 109 | call Say 'RRP004I Done.' 110 | rc = closeFile(hFileOut) 111 | exit 112 | 113 | /*-------------------------------------------------------------------* 114 | * Display an indented message 115 | *-------------------------------------------------------------------*/ 116 | 117 | Say: procedure expose g. 118 | parse arg sMessage 119 | say copies(' ',g.0LEVEL)sMessage 120 | return 121 | 122 | /*-------------------------------------------------------------------* 123 | * Open the file, or one nominated by the user 124 | *-------------------------------------------------------------------*/ 125 | 126 | getFile: procedure expose g. 127 | parse arg sFile,sMode 128 | if sFile <> '' 129 | then do 130 | sFile = strip(sFile) 131 | g.0hFile = openFile(sFile,sMode) 132 | do while g.0rc <> 0 & sFile <> '' 133 | call Say 'RPP006E Could not open file:' sFile 134 | call Say 'RPP007A Enter full path to file:' sFile 135 | parse pull sFile 136 | if sFile <> '' 137 | then g.0hFile = openFile(sFile,sMode) 138 | end 139 | end 140 | return sFile 141 | 142 | /*-------------------------------------------------------------------* 143 | * Recursively include files specified by 'INCLUDE' tags 144 | *-------------------------------------------------------------------*/ 145 | 146 | includeFile: procedure expose g. 147 | parse arg sFile,hFileIn,hFileOut 148 | if sFile = '' then return 149 | g.0LEVEL = g.0LEVEL + 2 150 | if g.0INCLUDED.sFile = 1 /* 20050601 */ 151 | then call Say 'RPP005I Already included file:' sFile /* 20050601 */ 152 | else do /* 20050601 */ 153 | g.0INCLUDED.sFile = 1 /* 20050601 */ 154 | call Say 'RPP002I Including file:' sFile 155 | sLine = getLine(hFileIn) 156 | do while g.0rc = 0 157 | select 158 | when left(sLine,10) = '/*INCLUDE ' 159 | then do 160 | parse var sLine '/*INCLUDE' sFile '*/' 161 | call include sFile,hFileOut 162 | end 163 | when left(sLine,11) = '/*INCLUDED ' 164 | then do 165 | parse var sLine '/*INCLUDED' sFile '*/' 166 | call exclude sFile,hFileIn 167 | call include sFile,hFileOut 168 | end 169 | otherwise g.0rc = putLine(hFileOut,sLine) 170 | end 171 | sLine = getLine(hFileIn) 172 | end 173 | rc = closeFile(hFileIn) 174 | call Say 'RRP004I Done.' 175 | end 176 | g.0LEVEL = g.0LEVEL - 2 177 | return 178 | 179 | /*-------------------------------------------------------------------* 180 | * Append the specified file to the output file 181 | *-------------------------------------------------------------------*/ 182 | 183 | include: procedure expose g. 184 | parse arg sFile,hFileOut 185 | sFile = strip(sFile) 186 | sFile = getFile(sFile) /* open it, else ask user */ 187 | hFileIn = g.0hFile 188 | if g.0rc = 0 189 | then do 190 | g.0rc = putLine(hFileOut,'/*INCLUDED' sFile '*/') 191 | call includeFile sFile,hFileIn,hFileOut 192 | g.0rc = putLine(hFileOut,'/*INCLUDEZ' sFile '*/') 193 | end 194 | return 195 | 196 | /*-------------------------------------------------------------------* 197 | * Skip over the specified file in the input file 198 | *-------------------------------------------------------------------*/ 199 | 200 | exclude: procedure expose g. 201 | parse arg sFile,hFileIn 202 | sFile = strip(sFile) 203 | do until sFile = sFileIncluded | g.0rc <> 0 204 | sLine = getLine(hFileIn) 205 | if left(sLine,11) = '/*INCLUDEZ ' 206 | then parse var sLine '/*INCLUDEZ' sFileIncluded '*/' 207 | end 208 | return 209 | 210 | /*REXX 2.0.0.1 211 | 212 | Copyright (c) 2009, Andrew J. Armstrong 213 | All rights reserved. 214 | 215 | Redistribution and use in source and binary forms, with or without 216 | modification, are permitted provided that the following conditions are 217 | met: 218 | 219 | * Redistributions of source code must retain the above copyright 220 | notice, this list of conditions and the following disclaimer. 221 | * Redistributions in binary form must reproduce the above copyright 222 | notice, this list of conditions and the following disclaimer in 223 | the documentation and/or other materials provided with the 224 | distribution. 225 | 226 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 227 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 228 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 229 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 230 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 231 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 232 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 233 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 234 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 235 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 236 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 237 | 238 | */ 239 | 240 | /*REXX***************************************************************** 241 | ** ** 242 | ** NAME - IO ** 243 | ** ** 244 | ** FUNCTION - Simple I/O routines. ** 245 | ** ** 246 | ** API - The routines in this module are: ** 247 | ** ** 248 | ** openFile(filename,options,attrs) ** 249 | ** Opens the specified file with the specified options** 250 | ** and returns a file handle to be used in other I/O ** 251 | ** operations. By default the file will be opened for ** 252 | ** input. Specify 'OUTPUT' to open it for output. ** 253 | ** For TSO, you can specify any operand of the TSO ** 254 | ** ALLOCATE command in the third operand. For example:** 255 | ** rc = openFile('MY.FILE','OUTPUT','RECFM(F,B)' ** 256 | ** 'LRECL(80) BLKSIZE(27920)') ** 257 | ** ** 258 | ** closeFile(handle) ** 259 | ** Closes the file specified by 'handle' (which was ** 260 | ** returned by the openFile() routine. ** 261 | ** ** 262 | ** getLine(handle) ** 263 | ** Reads the next line from the file specified by ** 264 | ** 'handle'. ** 265 | ** ** 266 | ** putLine(handle,data) ** 267 | ** Appends the specified data to the file specified ** 268 | ** by 'handle'. ** 269 | ** ** 270 | ** ** 271 | ** AUTHOR - Andrew J. Armstrong ** 272 | ** ** 273 | ** HISTORY - Date By Reason (most recent at the top please) ** 274 | ** -------- --------------------------------------------- ** 275 | ** 20090822 AJA Changed from GPL to BSD license. ** 276 | ** 20061017 AJA Added support for UNIX environment. ** 277 | ** Tested on Ubuntu Linux 6.06 LTS. ** 278 | ** 20050930 AJA Initial version. ** 279 | ** ** 280 | **********************************************************************/ 281 | 282 | parse source . . sSourceFile . 283 | parse value sourceline(1) with . sVersion . 284 | say 'Simple Rexx I/O routines' sVersion 285 | say 'You cannot invoke this rexx by itself!' 286 | say 287 | say 'This rexx is a collection of subroutines to be called' 288 | say 'from your own rexx procedures. You should either:' 289 | say ' - Append this procedure to your own rexx procedure,' 290 | say ' or,' 291 | say ' - Append the following line to your rexx:' 292 | say ' /* INCLUDE' sSourceFile '*/' 293 | say ' ...and run the rexx preprocessor:' 294 | say ' rexxpp myrexx myrexxpp' 295 | say ' This will create myrexxpp by appending this file to myrexx' 296 | exit 297 | 298 | /*-------------------------------------------------------------------* 299 | * Open a file 300 | *-------------------------------------------------------------------*/ 301 | 302 | openFile: procedure expose g. 303 | parse arg sFile,sOptions,sAttrs 304 | hFile = '' 305 | select 306 | when g.0ENV = 'TSO' then do 307 | bOutput = wordpos('OUTPUT',sOptions) > 0 308 | bQuoted = left(sFile,1) = "'" 309 | if bQuoted then sFile = strip(sFile,,"'") 310 | parse var sFile sDataset'('sMember')' 311 | if sMember <> '' then sFile = sDataset 312 | if bQuoted then sFile = "'"sFile"'" 313 | if bOutput 314 | then 'LMINIT DATAID(hFile) DATASET(&sFile) ENQ(EXCLU)' 315 | else 'LMINIT DATAID(hFile) DATASET(&sFile)' 316 | if sMember <> '' 317 | then do /* Open a member of a PDS */ 318 | 'LMOPEN DATAID(&hFile) OPTION(INPUT)' /* Input initially */ 319 | /* ... can't update ISPF stats when opened for output */ 320 | g.0MEMBER.hFile = sMember 321 | 'LMMFIND DATAID(&hFile) MEMBER('sMember') STATS(YES)' 322 | if bOutput 323 | then do 324 | if rc = 0 325 | then g.0STATS.hFile = zlvers','zlmod','zlc4date 326 | else g.0STATS.hFile = '1,0,0000/00/00' 327 | 'LMCLOSE DATAID(&hFile)' 328 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 329 | end 330 | end 331 | else do /* Open a sequential dataset */ 332 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 333 | if rc <> 0 /* If dataset does not already exist... */ 334 | then do /* Create sequential dataset then open it */ 335 | 'LMCLOSE DATAID(&hFile)' 336 | 'LMFREE DATAID(&hFile)' 337 | address TSO 'ALLOCATE DATASET('sFile') NEW CATALOG', 338 | 'SPACE(5,15) TRACKS RECFM(V,B)', 339 | 'LRECL('g.0OPTION.WRAP.1 + 4')', 340 | 'BLKSIZE(27990)' sAttrs 341 | if bOutput 342 | then do 343 | 'LMINIT DATAID(hFile) DATASET(&sFile) ENQ(EXCLU)' 344 | 'LMOPEN DATAID(&hFile) OPTION(&sOptions)' 345 | end 346 | else do 347 | 'LMINIT DATAID(hFile) DATASET(&sFile)' 348 | 'LMOPEN DATAID(&hFile) OPTION(INPUT)' 349 | end 350 | end 351 | end 352 | g.0OPTIONS.hFile = sOptions 353 | g.0rc = rc /* Return code from LMOPEN */ 354 | end 355 | otherwise do 356 | if wordpos('OUTPUT',sOptions) > 0 357 | then junk = stream(sFile,'COMMAND','OPEN WRITE REPLACE') 358 | else junk = stream(sFile,'COMMAND','OPEN READ') 359 | hFile = sFile 360 | if stream(sFile,'STATUS') = 'READY' 361 | then g.0rc = 0 362 | else g.0rc = 4 363 | end 364 | end 365 | return hFile 366 | 367 | /*-------------------------------------------------------------------* 368 | * Read a line from the specified file 369 | *-------------------------------------------------------------------*/ 370 | 371 | getLine: procedure expose g. 372 | parse arg hFile 373 | sLine = '' 374 | select 375 | when g.0ENV = 'TSO' then do 376 | 'LMGET DATAID(&hFile) MODE(INVAR)', 377 | 'DATALOC(sLine) DATALEN(nLine) MAXLEN(32768)' 378 | g.0rc = rc 379 | sLine = strip(sLine,'TRAILING') 380 | if sLine = '' then sLine = ' ' 381 | end 382 | otherwise do 383 | g.0rc = 0 384 | if chars(hFile) > 0 385 | then sLine = linein(hFile) 386 | else g.0rc = 4 387 | end 388 | end 389 | return sLine 390 | 391 | /*-------------------------------------------------------------------* 392 | * Append a line to the specified file 393 | *-------------------------------------------------------------------*/ 394 | 395 | putLine: procedure expose g. 396 | parse arg hFile,sLine 397 | select 398 | when g.0ENV = 'TSO' then do 399 | g.0LINES = g.0LINES + 1 400 | 'LMPUT DATAID(&hFile) MODE(INVAR)', 401 | 'DATALOC(sLine) DATALEN('length(sLine)')' 402 | end 403 | otherwise do 404 | junk = lineout(hFile,sLine) 405 | rc = 0 406 | end 407 | end 408 | return rc 409 | 410 | /*-------------------------------------------------------------------* 411 | * Close the specified file 412 | *-------------------------------------------------------------------*/ 413 | 414 | closeFile: procedure expose g. 415 | parse arg hFile 416 | rc = 0 417 | select 418 | when g.0ENV = 'TSO' then do 419 | if g.0MEMBER.hFile <> '', /* if its a PDS */ 420 | & wordpos('OUTPUT',g.0OPTIONS.hFile) > 0 /* opened for output */ 421 | then do 422 | parse value date('STANDARD') with yyyy +4 mm +2 dd +2 423 | parse var g.0STATS.hFile zlvers','zlmod','zlc4date 424 | zlcnorc = min(g.0LINES,65535) /* Number of lines */ 425 | nVer = right(zlvers,2,'0')right(zlmod,2,'0') /* vvmm */ 426 | nVer = right(nVer+1,4,'0') /* vvmm + 1 */ 427 | parse var nVer zlvers +2 zlmod +2 428 | if zlc4date = '0000/00/00' 429 | then zlc4date = yyyy'/'mm'/'dd /* Creation date */ 430 | zlm4date = yyyy'/'mm'/'dd /* Modification date */ 431 | zlmtime = time() /* Modification time */ 432 | zluser = userid() /* Modification user */ 433 | 'LMMREP DATAID(&hFile) MEMBER('g.0MEMBER.hFile') STATS(YES)' 434 | end 435 | 'LMCLOSE DATAID(&hFile)' 436 | 'LMFREE DATAID(&hFile)' 437 | end 438 | otherwise do 439 | if stream(hFile,'COMMAND','CLOSE') = 'UNKNOWN' 440 | then rc = 0 441 | else rc = 4 442 | end 443 | end 444 | return rc -------------------------------------------------------------------------------- /soap.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /* REXX *************************************************************** 31 | ** ** 32 | ** NAME - SOAP ** 33 | ** ** 34 | ** FUNCTION - This exec invokes a Web Service and displays the ** 35 | ** response. It assumes you are running the Apache Axis ** 36 | ** SOAP server on the specified host and port. Other SOAP ** 37 | ** servers should also work but have not been tested. ** 38 | ** ** 39 | ** Each time you invoke the client, you specify the SOAP ** 40 | ** server hostname and port, and the name of the service ** 41 | ** that you want to use. The client will then retrieve ** 42 | ** the Web Services Definition Language (WSDL) definitions** 43 | ** for that service from the SOAP server. If there is ** 44 | ** only one operation defined for that service then that ** 45 | ** operation is invoked automatically, else you will be ** 46 | ** prompted to select the operation to be invoked. If the ** 47 | ** operation requires one or more parameters then you ** 48 | ** will be prompted for each parameter. No attempt is ** 49 | ** made to validate the input you supply. The operation ** 50 | ** is invoked and the response is displayed. ** 51 | ** ** 52 | ** NOTES - 1. You will have to either append the PARSEXML and ** 53 | ** PRETTY source files manually to this file, or run ** 54 | ** this file through the REXX rexx pre-processor. ** 55 | ** ** 56 | ** To use the pre-processor, run: ** 57 | ** ** 58 | ** tso rexxpp your.rexx.lib(soap) ** 59 | ** ** 60 | ** 2. You can download Apache Axis from: ** 61 | ** http://ws.apache.org/axis/ ** 62 | ** ** 63 | ** 3. Start the Apache Axis server by issuing the ** 64 | ** following command (all on one line with no line ** 65 | ** breaks): ** 66 | ** ** 67 | ** java org.apache.axis.transport.http.SimpleAxisServer** 68 | ** -p 8080 ** 69 | ** ** 70 | ** ...you will need to have the Axis jar files in your ** 71 | ** classpath, of course. ** 72 | ** ** 73 | ** 4. If you need to go through a proxy server, then ** 74 | ** specify the proxy server hostname and port number ** 75 | ** in the z.!PROXYHOST and z.!PROXYPORT variables ** 76 | ** (see below). ** 77 | ** ** 78 | ** 5. If you have access to the Internet, you can try the ** 79 | ** SOAP client out by running: ** 80 | ** ** 81 | ** soap vizier.cfa.harvard.edu:8080 UCD ** 82 | ** ** 83 | ** ** 84 | ** ** 85 | ** ** 86 | ** SYNTAX - SOAP url [service] [(options...] ** 87 | ** ** 88 | ** Where, ** 89 | ** url = The URL of the Axis SOAP server. Format is:** 90 | ** hostname:port/path ** 91 | ** hostname - Axis SOAP server hostname ** 92 | ** port - default is 80 ** 93 | ** path - default is axis/services ** 94 | ** service = Service name (default is Version). This ** 95 | ** name is case-sensitive. ** 96 | ** options = One or more options: ** 97 | ** TRACE - Trace network I/O ** 98 | ** DEBUG - Dump message contents in hex ** 99 | ** XML - Display messages contents in XML ** 100 | ** NOPROXY - Do not use proxy host. ** 101 | ** ** 102 | ** AUTHOR - Andrew J. Armstrong ** 103 | ** ** 104 | ** HISTORY - Date By Reason (most recent at the top please) ** 105 | ** -------- --- ----------------------------------------- ** 106 | ** 20090822 AJA Changed from GPL to BSD license. ** 107 | ** 20050602 AJA Retrieve WSDL from SOAP server and prompt ** 108 | ** user for the operation and parameters. ** 109 | ** 20050601 AJA Added proxy host support. ** 110 | ** 20050531 AJA Original version. ** 111 | ** ** 112 | **********************************************************************/ 113 | 114 | parse arg sURL sService . '('sOptions')' 115 | sOptions = translate(sOptions) /* convert to upper case */ 116 | 117 | parse value sourceline(1) with . sVersion 118 | say 'SOAP000I Rexx SOAP client' sVersion 119 | 120 | z. = '' /* g. is used by the XML parser */ 121 | 122 | z.!PROXYHOST = 'proxy.example.org' 123 | z.!PROXYPORT = '8080' 124 | 125 | if pos('://',sURL) > 0 126 | then parse var sURL sScheme'://'sURL /* ignore protocol (scheme) */ 127 | parse var sURL sHost'/'sPath 128 | parse var sHost sHost':'nPort 129 | if sHost = '' then sHost = 'axisserver.example.org' 130 | if \datatype(nPort,'WHOLE') then nPort = 80 131 | if sPath = '' then sPath = 'axis/services' 132 | if sService = '' then sService = 'Version' 133 | 134 | say 'SOAP001I Host='sHost 135 | say 'SOAP002I Port='nPort 136 | say 'SOAP003I Path='sPath 137 | say 'SOAP004I Service='sService 138 | 139 | call initParser 'NOBLANKS' /* <-- This is in PARSEXML rexx */ 140 | z.!DEBUG = wordpos('DEBUG',sOptions) <> 0 141 | z.!TRACE = wordpos('TRACE',sOptions) <> 0 142 | z.!XML = wordpos('XML',sOptions) <> 0 143 | z.!NOPROXY = wordpos('NOPROXY',sOptions) <> 0 144 | 145 | if z.!NOPROXY 146 | then do 147 | z.!PROXYHOST = '' 148 | z.!PROXYPORT = '' 149 | end 150 | 151 | say 'SOAP005I Proxy Host='z.!PROXYHOST 152 | say 'SOAP006I Proxy Port='z.!PROXYPORT 153 | call Prolog 154 | 155 | /* 156 | *--------------------------------------------------------------------* 157 | * Contact the SOAP server and retrieve the WSDL for this service 158 | *--------------------------------------------------------------------* 159 | */ 160 | nSocket = Connect(sHost,nPort) 161 | say 'SOAP007I Retrieving WSDL for' sService 'service' 162 | if z.!PROXYHOST <> '' 163 | then sURL = 'http://'sHost':'nPort'/'sPath'/'sService'?wsdl' 164 | else sURL = '/'sPath'/'sService'?wsdl' 165 | say 'SOAP008I GET' sURL 166 | sHeader = 'GET' sURL 'HTTP/1.1' || z.!CRLFCRLF 167 | 168 | sMsg = sHeader 169 | if z.!DEBUG then call Dump sMsg,'Sending' 170 | sWrite = write(nSocket,sMsg) 171 | /* 172 | *--------------------------------------------------------------------* 173 | * Read the response from the SOAP server 174 | *--------------------------------------------------------------------* 175 | */ 176 | say 'SOAP009I Reading WSDL response' 177 | sReply = Slurp(nSocket) 178 | sDisc = Disconnect(nSocket) 179 | parse var sReply sHeader (z.!CRLFCRLF) sXML 180 | if z.!DEBUG then call Dump sHeader,'Header' 181 | if z.!DEBUG then call Dump sXML,'Payload' 182 | parse var sHeader . nCode . 0 . sReason (z.!CRLF) 183 | if nCode <> 200 184 | then call Abort 'SOAP021E HTTP GET failed:' sReason 185 | /* 186 | *--------------------------------------------------------------------* 187 | * Parse the WSDL 188 | *--------------------------------------------------------------------* 189 | */ 190 | rc = parseString(sXML) 191 | doc = getDocumentElement() 192 | if getName(doc) <> 'wsdl:definitions' 193 | then do 194 | say 'SOAP010E Could not retrieve WSDL for' sService, 195 | 'service on' sHost 196 | say 'SOAP011I The reply received was:' 197 | call showResponse doc 198 | call Abort 199 | end 200 | porttype = getChildrenByName(doc,'wsdl:portType') 201 | operations = getChildrenByName(porttype,'wsdl:operation') 202 | messages = getChildrenByName(doc,'wsdl:message') 203 | do i = 1 to words(messages) 204 | message = word(messages,i) 205 | sMsgName = getAttribute(message,'name') 206 | z.!MSG.sMsgName = message 207 | z.!MSG.message = sMsgName 208 | end 209 | z.!OP.0 = words(operations) /* number of operations */ 210 | if z.!OP.0 = 0 211 | then call Abort 'Service' sService 'supports no operations' 212 | do i = 1 to z.!OP.0 /* for each operation of this service...*/ 213 | operation = word(operations,i) 214 | input = getChildrenByName(operation,'wsdl:input') 215 | output = getChildrenByName(operation,'wsdl:output') 216 | sInputMsgName = getAttribute(input,'name') 217 | sOutputMsgName = getAttribute(output,'name') 218 | z.!OP.i = operation /* operation node */ 219 | z.!IN.i = z.!MSG.sInputMsgName /* input parms node */ 220 | z.!OUT.i = z.!MSG.sOutputMsgName /* output parms node */ 221 | end 222 | 223 | if z.!OP.0 > 1 /* More than one operation to choose from? */ 224 | then do 225 | say 'SOAP012A Which' sService 'operation do you want to invoke?' 226 | do i = 1 to z.!OP.0 227 | say i'.' getAttribute(z.!OP.i,'name') 228 | end 229 | pull n 230 | if n = '' then call Abort 'Operation cancelled' 231 | if \datatype(n,'WHOLE') | n < 1 | n > z.!OP.0 then n = 1 232 | end 233 | else n = 1 234 | 235 | sOperation = getAttribute(z.!OP.n,'name') 236 | sExpectedResponse = getAttribute(z.!OUT.n,'name') 237 | say 'SOAP013I About to invoke operation:' sOperation 238 | parameters = getChildrenByName(z.!IN.n,'wsdl:part') 239 | z.!PARM.0 = words(parameters) /* number of defined parameters */ 240 | do i = 1 to z.!PARM.0 241 | parameter = word(parameters,i) 242 | sParmName = getAttribute(parameter,'name') 243 | sParmType = getAttribute(parameter,'type') 244 | say 'SOAP014A Enter value for' sParmName '(type is' sParmType'):' 245 | parse pull reply 246 | z.!PARM.i = reply 247 | z.!PARMNAME.i = sParmName 248 | end 249 | 250 | /* 251 | *--------------------------------------------------------------------* 252 | * Create a SOAP request message roughly like: 253 | * 254 | * 255 | * 256 | * 257 | * 258 | *--------------------------------------------------------------------* 259 | */ 260 | call initParser 'NOBLANKS' 261 | doc = createDocument('soapenv:Envelope') 262 | call setAttribute doc,'xmlns:soapenv',, 263 | 'http://schemas.xmlsoap.org/soap/envelope/' 264 | call setAttribute doc,'xmlns:xsd',, 265 | 'http://www.w3.org/2001/XMLSchema' 266 | call setAttribute doc,'xmlns:xsi',, 267 | 'http://www.w3.org/2001/XMLSchema-instance' 268 | body = createElement('soapenv:Body') 269 | call appendChild body,doc 270 | op = createElement(sOperation) 271 | call appendChild op,body 272 | do i = 1 to z.!PARM.0 273 | sParmName = z.!PARMNAME.i 274 | sParmValue = z.!PARM.i 275 | parm = createElement(sParmName) 276 | call appendChild createTextNode(sParmValue),parm 277 | call appendChild parm,op 278 | end 279 | nSocket = Connect(sHost,nPort) 280 | sContent = toString(getRoot()) 281 | say 'SOAP015I Sending' sOperation 'message to' sService 'service' 282 | if z.!XML then say sContent 283 | /* 284 | *--------------------------------------------------------------------* 285 | * Build an HTTP header and send the message 286 | *--------------------------------------------------------------------* 287 | */ 288 | if z.!PROXYHOST <> '' 289 | then sURL = 'http://'sHost':'nPort'/'sPath'/'sService 290 | else sURL = '/'sPath'/'sService 291 | say 'SOAP020I POST' sURL 292 | sHeader = 'POST' sURL 'HTTP/1.0' || z.!CRLF ||, 293 | 'Content-Type: text/xml; charset=utf-8' || z.!CRLF ||, 294 | 'Accept: application/soap+xml, application/dime,', 295 | 'multipart/related, text/*' || z.!CRLF ||, 296 | 'User-Agent: EpistAxis/1.2' || z.!CRLF ||, 297 | 'Host:' sHost':'nPort || z.!CRLF ||, 298 | 'Cache-Control: no-cache' || z.!CRLF ||, 299 | 'Pragma: no-cache' || z.!CRLF ||, 300 | 'SOAPAction: ""' || z.!CRLF ||, 301 | 'Content-Length:' length(sContent) || z.!CRLF || z.!CRLF 302 | 303 | sMsg = sHeader || sContent 304 | if z.!DEBUG then call Dump sMsg,'Sending' 305 | sWrite = write(nSocket,sMsg) 306 | /* 307 | *--------------------------------------------------------------------* 308 | * Read the response from the SOAP server 309 | *--------------------------------------------------------------------* 310 | */ 311 | sReply = Slurp(nSocket) 312 | parse var sReply sHeader (z.!CRLFCRLF) sXML 313 | if z.!DEBUG then call Dump sHeader,'Header' 314 | if z.!DEBUG then call Dump sXML,'Payload' 315 | parse var sHeader . nCode . 0 . sReason (z.!CRLF) 316 | if nCode <> 200 317 | then call Abort 'SOAP021E HTTP POST failed:' sReason 318 | 319 | /* 320 | *--------------------------------------------------------------------* 321 | * Now parse the response message. 322 | * If it worked, the response will look like: 323 | * 324 | * 325 | * 326 | * 327 | * 328 | * version information 329 | * 330 | * 331 | * 332 | * 333 | * 334 | * If it failed, the response will look like: 335 | * 336 | * 337 | * 338 | * 339 | * code 340 | * string 341 | * 342 | * stack trace information 343 | * hostname of SOAP server 344 | * 345 | * 346 | * 347 | * 348 | *--------------------------------------------------------------------* 349 | */ 350 | call initParser 'NOBLANKS' 351 | rc = parseString(sXML) 352 | if z.!XML then call prettyPrinter 353 | 354 | /* 355 | *--------------------------------------------------------------------* 356 | * Extract the payload from the response 357 | *--------------------------------------------------------------------* 358 | */ 359 | doc = getDocumentElement() /* SOAP envelope */ 360 | body = getFirstChild(doc) /* SOAP body */ 361 | resp = getFirstChild(body) /* SOAP server response */ 362 | select 363 | when getName(resp) = sExpectedResponse then do 364 | say 'SOAP016I Received "'sExpectedResponse'" response:' 365 | call showResponse resp 366 | end 367 | when getName(resp) = 'soapenv:Fault' then do 368 | say 'SOAP017E Received "'getName(resp)'" response:' 369 | call showResponse resp 370 | end 371 | otherwise do 372 | say 'SOAP018W Received unexpected response:' 373 | say toString(resp) 374 | end 375 | end 376 | 377 | call Epilog 378 | exit 379 | 380 | showResponse: procedure expose z. g. 381 | parse arg node,sPad 382 | if isElementNode(node) 383 | then do 384 | if hasAttribute(node,'xsi:type') 385 | then do 386 | say sPad || getName(node), 387 | getAttribute(node,'xsi:type') ||, 388 | '="'getText(getFirstChild(node))'"' 389 | end 390 | else do 391 | say sPad || getName(node) 392 | if isTextNode(getFirstChild(node)) 393 | then say sPad' 'getText(getFirstChild(node)) 394 | end 395 | end 396 | children = getChildren(node) 397 | do i = 1 to words(children) 398 | child = word(children,i) 399 | call showResponse child,sPad' ' 400 | end 401 | return 402 | 403 | showParameters: procedure expose z. g. 404 | parse arg sMsgName 405 | message = z.!MSG.sMsgName 406 | parts = getChildrenByName(message,'wsdl:part') 407 | do k = 1 to words(parts) 408 | part = word(parts,k) 409 | say ' parameter:' getAttribute(part,'name'), 410 | 'type:' getAttribute(part,'type') 411 | end 412 | return 413 | 414 | Connect: procedure expose z. 415 | parse arg sHost,nPort 416 | say 'SOAP019I Connecting to' sHost 'port' nPort 417 | if z.!PROXYHOST <> '' then sHost = z.!PROXYHOST 418 | if z.!PROXYPORT <> '' then nPort = z.!PROXYPORT 419 | sShutdown = Socket('Shutdown', z.!SOCKET) 420 | sClose = Socket('Close', z.!SOCKET) 421 | nSocket = Socket('Socket','AF_INET','SOCK_STREAM','TCP') 422 | sSockOpt = Socket('SetSockOpt',nSocket,'IPPROTO_TCP','SO_ASCII','ON') 423 | sConnect = Socket('Connect',nSocket,'AF_INET' nPort sHost) 424 | if z.!RC <> 0 then call ABORT sConnect 425 | return nSocket 426 | 427 | Slurp: procedure expose z. 428 | parse arg nSocket 429 | sReply = '' 430 | sRead = read(nSocket) 431 | do while length(sRead) > 0 432 | sReply = sReply || sRead 433 | sRead = read(nSocket) 434 | end 435 | return sReply 436 | 437 | Disconnect: procedure expose z. 438 | parse arg nSocket 439 | sShutdown= Socket('Shutdown', nSocket) 440 | sResp = Socket('Close', nSocket) 441 | return sResp 442 | 443 | Abort: procedure expose z. 444 | parse arg sMsg 445 | say sMsg 446 | call Epilog 447 | exit 448 | return 449 | 450 | Write: procedure expose z. 451 | parse arg nSocket,sMsg 452 | if z.!TRACE then call Dump sMsg,'-->' 453 | sWrite = Socket('Write',nSocket,sMsg) 454 | return sWrite 455 | 456 | Read: procedure expose z. 457 | parse arg nSocket 458 | sRead = Socket('Read',nSocket) 459 | parse var sRead nLen sData 460 | if z.!TRACE then call Dump sData,'<--' 461 | return sData 462 | 463 | Dump: procedure expose z. 464 | parse arg sData,sPrefix,nPad 465 | if \datatype(nPad,'WHOLE') then nPad = 8 466 | sPrefix = left(sPrefix,nPad) 467 | lastline = length(sData)%16*16+1 468 | do i = 1 to length(sData) by 16 469 | if i = lastline 470 | then sChunk = substr(sData,i) 471 | else sChunk = substr(sData,i,16) 472 | xChunk = left(c2x(sChunk),32) 473 | say sPrefix, 474 | substr(xChunk, 1,8), 475 | substr(xChunk, 9,8), 476 | substr(xChunk,17,8), 477 | substr(xChunk,25,8), 478 | '*'left(sChunk,16)'*' 479 | sPrefix = right('+'d2x(i+15,6),nPad) 480 | end 481 | return 482 | 483 | Prolog: 484 | z.!CR = '0D'x /* EBCDIC Carriage Return */ 485 | z.!LF = '25'x /* EBCDIC Line Feed */ 486 | z.!CRLF = '0D25'x 487 | z.!CRLFCRLF = z.!CRLF || z.!CRLF 488 | sResp = Socket('Terminate') /* Kill any previous socket set */ 489 | sResp = Socket('Initialize', 'MySet') 490 | if z.!RC <> 0 then exit z.!RC 491 | return 492 | 493 | Epilog: 494 | sDisc = Disconnect(nSocket) 495 | sResp = Socket('Terminate') 496 | return sResp 497 | 498 | Socket: procedure expose z. 499 | parse arg a,c,d,e,f,g,h,i,j,k 500 | if z.!TRACE 501 | then say 'Socket('strip(a','c','d','e','f','g','h','i','j','k,'T',",")')' 502 | 503 | parse value 'SOCKET'(a,c,d,e,f,g,h,i,j,k) with nRC sResp 504 | z.!RC = nRC 505 | 506 | if z.!TRACE 507 | then do 508 | say ' Return code <'nRC'>' 509 | say ' Response <'sResp'>' 510 | end 511 | return sResp 512 | 513 | /*INCLUDE pretty.rex */ 514 | -------------------------------------------------------------------------------- /test.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 7 | 8 | 9 | Andrew 10 | 11 | Armstrong 12 | Scumbag&tm; 13 | Sturm & Drang 14 | 15 | Some unparsed character data: 16 | 17 | Andrew Armstrong]]> 18 | 19 | Test text: and/or & && &tm;includes 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /testmod.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - TESTMOD ** 33 | ** ** 34 | ** FUNCTION - Tests modification of an existing XML file. ** 35 | ** ** 36 | ** ** 37 | ** SYNTAX - testmod infile [outfile] ** 38 | ** ** 39 | ** Where, ** 40 | ** infile = Name of XML file to be modified. ** 41 | ** outfile = Name of file to store the output in. ** 42 | ** The default is the console. ** 43 | ** ** 44 | ** ** 45 | ** NOTES - 1. You will have to either append the PRETTY rexx code ** 46 | ** manually to this demo source, or run this demo ** 47 | ** source through the REXXPP rexx pre-processor. ** 48 | ** ** 49 | ** To use the pre-processor, run: ** 50 | ** ** 51 | ** rexxpp testmod testmodp ** 52 | ** ** 53 | ** ...and then run the resulting rexx procedure over ** 54 | ** an XML file of your choice: ** 55 | ** ** 56 | ** testmodp infile ** 57 | ** ...or... ** 58 | ** testmodp infile outfile ** 59 | ** ** 60 | ** ** 61 | ** AUTHOR - Andrew J. Armstrong ** 62 | ** ** 63 | ** HISTORY - Date By Reason (most recent at the top pls) ** 64 | ** -------- --- ----------------------------------------- ** 65 | ** 20090822 AJA Changed from GPL to BSD license. ** 66 | ** 20040707 AJA Initial version. ** 67 | ** ** 68 | **********************************************************************/ 69 | 70 | 71 | parse arg sFileIn sFileOut' ('sOptions')' 72 | 73 | parse value sourceline(1) with . sVersion 74 | say 'Unit Test Suite 1 - ' sVersion 75 | 76 | if sFileIn = '' 77 | then do 78 | parse source sSystem sInvocation sSourceFile . 79 | say 'Syntax:' sSourceFile 'filein fileout (options...' 80 | exit 81 | end 82 | 83 | 84 | sOptions = 'NOBLANKS' sOptions 85 | call initParser sOptions /* <-- This is in PARSEXML rexx */ 86 | 87 | parse source g.!ENV . 88 | if g.!ENV = 'TSO' 89 | then do 90 | address ISPEXEC 91 | 'CONTROL ERRORS RETURN' 92 | g.!LINES = 0 93 | end 94 | 95 | /* Open the specified file and parse it */ 96 | nParseRC = parseFile(sFileIn) 97 | doc = getDocumentElement() 98 | 99 | /* Create a new tag */ 100 | prologNode = createElement('prolog') 101 | faqNode = createElement('faq') 102 | call setAttribute faqNode,'question','What is your name?' 103 | textNode = createTextNode('Sir Gawain of Camelot') 104 | call appendChild textNode,faqNode 105 | call appendChild faqNode,prologNode 106 | /* We now have the following structure in memory: 107 | 108 | 109 | Sir Gawain of Camelot 110 | 111 | 112 | */ 113 | 114 | call Log 'Inserting a tag at the start of the document' 115 | firstChild = getFirstChild(doc) 116 | if firstChild <> '' /* If document has a first child */ 117 | then call insertBefore prologNode,firstChild 118 | else call appendChild prologNode,doc 119 | 120 | /* Verify that the first child is now indeed the node */ 121 | call assertEquals getFirstChild(doc),prologNode,, 122 | 'Failed to insert as the first child' 123 | 124 | call Log 'Appending an empty at the end of the document' 125 | call appendChild createElement('epilog'),doc 126 | 127 | /* Save the document to a file (or display on console) */ 128 | call prettyPrinter sFileOut 129 | exit 130 | 131 | Log: procedure expose g. 132 | parse arg sMessage 133 | g.!TEST = g.!TEST + 1 /* increment test number */ 134 | say 135 | say 'Test' right(g.!TEST,3,'0') left(sMessage,68,'-') 136 | return 137 | 138 | /*INCLUDE pretty.rex */ 139 | /*INCLUDE assert.rex */ 140 | -------------------------------------------------------------------------------- /testnew.rex: -------------------------------------------------------------------------------- 1 | /*REXX 2.0.0 2 | Copyright (c) 2009-2020, Andrew J. Armstrong 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | /*REXX***************************************************************** 31 | ** ** 32 | ** NAME - TESTNEW ** 33 | ** ** 34 | ** FUNCTION - Tests creation and modification of XML. ** 35 | ** ** 36 | ** ** 37 | ** SYNTAX - testnew outfile ** 38 | ** ** 39 | ** Where, ** 40 | ** outfile = Name of file to store the output in. ** 41 | ** The default is the console. ** 42 | ** ** 43 | ** ** 44 | ** NOTES - 1. You will have to either append the PRETTY rexx code ** 45 | ** manually to this demo source, or run this demo ** 46 | ** source through the REXXPP rexx pre-processor. ** 47 | ** ** 48 | ** To use the pre-processor, run: ** 49 | ** ** 50 | ** rexxpp testnew testnewp ** 51 | ** ** 52 | ** ...and then run the resulting rexx procedure: ** 53 | ** ** 54 | ** testnewp ** 55 | ** ...or... ** 56 | ** testnewp outfile ** 57 | ** ** 58 | ** ** 59 | ** AUTHOR - Andrew J. Armstrong ** 60 | ** ** 61 | ** HISTORY - Date By Reason (most recent at the top please) ** 62 | ** -------- --- ----------------------------------------- ** 63 | ** 20090822 AJA Changed from GPL to BSD license. ** 64 | ** 20060803 AJA Added unit tests for getAttributeMap. ** 65 | ** 20040707 AJA Initial version. ** 66 | ** ** 67 | **********************************************************************/ 68 | 69 | 70 | parse arg sFileOut' ('sOptions 71 | 72 | parse value sourceline(1) with . sVersion 73 | say 'Unit Test Suite 2 - ' sVersion 74 | 75 | sOptions = 'NOBLANKS' sOptions 76 | call initParser /* <-- This is in PARSEXML rexx */ 77 | 78 | parse source g.!ENV . 79 | if g.!ENV = 'TSO' 80 | then do 81 | address ISPEXEC 82 | 'CONTROL ERRORS RETURN' 83 | g.!LINES = 0 84 | end 85 | 86 | g.!TEST = 0 87 | 88 | call Log 'Create a new document' 89 | doc = createDocument('bridgekeeper') 90 | call prettyPrinter 91 | 92 | call Log 'Create a faq tag and append it to the document' 93 | q1 = createElement('faq') 94 | call appendChild q1,doc 95 | count = words(getChildren(doc)) 96 | call assertEquals 1,count,'001 has wrong child count' 97 | call setAttribute q1,'question','What is your name?' 98 | count = getAttributeCount(q1) 99 | call assertEquals 1,count,'002 element has wrong attr count' 100 | textNode = createTextNode('Sir Gawain of Camelot') 101 | call appendChild textNode,q1 102 | count = words(getChildren(q1)) 103 | call assertEquals 1,count,'003 element has wrong child count' 104 | call prettyPrinter 105 | 106 | call Log 'Create another faq tag and append it to the document' 107 | q2 = createElement('faq') 108 | call setAttribute q2,'question','What is your quest?' 109 | count = getAttributeCount(q2) 110 | call assertEquals 1,count,'004 element has wrong attr count' 111 | call setAttribute q2,'answer','To seek the Holy Grail' 112 | count = getAttributeCount(q2) 113 | call assertEquals 2,count,'005 element has wrong attr count' 114 | call appendChild q2,doc 115 | count = words(getChildren(doc)) 116 | call assertEquals 2,count,'006 has wrong child count' 117 | call prettyPrinter 118 | 119 | call Log 'Create yet another faq tag and append it to the document' 120 | q3 = createElement('faq') 121 | call setAttribute q3,'question','What is your favourite color?' 122 | call setAttribute q3,'answer','Blue' 123 | call appendChild q3,doc 124 | count = words(getChildren(doc)) 125 | call assertEquals 3,count,'007 has wrong child count' 126 | call prettyPrinter 127 | 128 | call Log 'Modify the answer attribute on the third faq tag' 129 | call setAttribute q3,'answer','No yelloooooww' 130 | call assertEquals 'No yelloooooww',getAttribute(q3,'answer'),, 131 | '008 "answer" attribute of is not correct' 132 | call prettyPrinter 133 | 134 | call Log 'Remove a text node and some attributes' 135 | count = words(getChildren(q1)) 136 | call assertEquals 1,count,'009 has wrong child count' 137 | call removeChild textNode /* give Sir Gawain the flick */ 138 | count = words(getChildren(q1)) 139 | call assertEquals 0,count,'010 has wrong child count' 140 | 141 | count = getAttributeCount(q3) 142 | call assertEquals 2,count,'011 element has wrong attr count' 143 | call removeAttribute q3,'question' 144 | count = getAttributeCount(q3) 145 | call assertEquals 1,count,'012 element has wrong attr count' 146 | call removeAttribute q3,'answer' 147 | count = getAttributeCount(q3) 148 | call assertEquals 0,count,'013 element has wrong attr count' 149 | call prettyPrinter 150 | 151 | call Log 'Insert a new text node in the first faq tag' 152 | textNode = createTextNode('King of the Britons') 153 | call appendChild textNode,q1 154 | call insertBefore createTextNode('It is Arthur, '),textNode 155 | call setAttribute q3,'question','What is the air-speed velocity', 156 | 'of an unladen swallow?' 157 | call setAttribute q3,'answer','What do you mean? An African or', 158 | 'European swallow?' 159 | call prettyPrinter 160 | 161 | call Log 'Exercise getAttributeMap' 162 | call prettyPrinter ,,q3 163 | call getAttributeMap q3 164 | count = g.!ATTRIBUTE.0 165 | call assertEquals 2,count,'014 element has wrong attr count' 166 | s = g.!ATTRIBUTE.1 167 | call assertEquals 'question',s,, 168 | '015 Attribute 1 of has wrong name' 169 | s = g.!ATTRIBUTE.2 170 | call assertEquals 'answer',s,, 171 | '016 Attribute 2 of has wrong name' 172 | 173 | call Log 'Write the document to a file (or console)' 174 | call prettyPrinter sFileOut 175 | exit 176 | 177 | Log: procedure expose g. 178 | parse arg sMessage 179 | g.!TEST = g.!TEST + 1 /* increment test number */ 180 | say 181 | say 'Test' right(g.!TEST,3,'0') left(sMessage,68,'-') 182 | return 183 | 184 | /*INCLUDE pretty.rex */ 185 | /*INCLUDE assert.rex */ 186 | --------------------------------------------------------------------------------