Fosdick H. Rexx Programmer's Reference 2ed 2025
Fosdick H. Rexx Programmer's Reference 2ed 2025
Runs everywhere
and interfaces to everything. That’s Rexx.
You’ll learn -
THE
LANGUAGE
AGSGCIATinN
Rexxlnfo.org
RexxLA.org
Rexx Programmer’s Reference, 2nd Edition
Howard Fosdick
Rexx Programmer’s Reference, 2nd Edition
Published by Rexx Language Association
8525 Pinefield Road,
Apex, NC 27523-9601, USA
The Rexx Language Association is a Non-Profit Corporation registered in North Carolina, USA, incorporated
in 1998. Visit RexxLA.org.
ISBN 978-9-40374-552-7
This is a fully revised 2nd edition of the book with the same title published in 2005 by Wiley Publishing, Inc.
This updated and revised reprint is published under authority of clause 18 of the publishing contract dated July 8, 2004
between Wiley Publishing Inc., and Fosdick Consulting Inc., and subsequent letter dated December 14, 2015, affirming right
to reprint, from Fosdick Consulting Inc. to Wiley Publishing Inc., as pursuant to that contract.
TRADEMARKS: All trademarks are the property of their respective owners. The Rexx Language
Association is not associated with any vendor mentioned in this book.
About the Author
Howard Fosdick is an independent IT consultant who supports databases and operating systems. He's
written seven books and over 500 articles and frequently speaks at conferences. He co-founded the International
DB2 Users Group, the Midwest Database Users Group, and CAMP IT, and invented concepts like hype curves
and open consulting. While he’s coded in many programming languages, Rexx remains his favorite.
Rexx is a very underrated programming language; elegant in design, simple syntax, easy to learn, use
and maintain, yet as powerful as any other scripting language available today.
In 1979, Mike Cowlishaw, IBM fellow, designed a “human-centric” programming language, Rexx.
Cowlishaw’s premise was that the programmer should not have to tell the interpreter what the
language syntax was in each program they wrote; that was the job of the interpreter. So unlike most
other programming languages, Rexx does not suffer from superfluous, meaningless punctuation
characters throughout the code.
Since the release of Rexx outside of IBM, Rexx has been ported to virtually all operating systems and
was formally standardised with the publishing of the ANSI Standard for Rexx in 1996. In late 2004, IBM
transferred their implementation of Object REXX to the Rexx Language Association under an Open
Source license. This event signalled a new era in the history of Rexx.
This book provides a comprehensive reference and programming guide to the Rexx programming lan
guage. It shows how to use the most popular implementations of Rexx and Rexx external function pack
ages and is suited to both the programmer learning Rexx for the first time as well as the seasoned Rexx
developer requiring a single, comprehensive reference manual.
Rexx has had a major influence on my life for the past 20 years since I wrote my first XEDIT macro in
Rexx. In the last 10 years I have maintained the Regina Rexx interpreter, ably assisted by Florian Grofie-
Coosmann, and in my remaining spare time have developed several Rexx external function packages
(and my XEDIT-like text editor, THE). However, like many developers of open source products, I have
never quite documented the products as completely as they deserve.
This is the book I would have liked to write if I had had the time. I’m glad Howard had the time!
Mark Hessling
The release of the 2nd edition of The Rexx Programmer’s Reference is a testament to the passion that
Howard and the majority of users have for Rexx and the longevity of the Rexx programming
language itself.
As software technologies change over the years, Rexx easily incorporates those new technologies
due to its exceptional design. This flexibility to change does not come at the expense of breakage of
existing code as is so common in most other programming languages. As a result, new program
development using Rexx is as viable now as it was when Rexx first appeared, and existing code still
runs unchanged as reliably now as when it was first developed.
This edition includes new content and changes that reflect the current state of Rexx.
I hope I’m around for the 3rd edition of The Rexx Programmer’s Reference!
Mark Hessling
www.Rexx.org
Acknowledgments for the 1st Edition
Special thanks are due to Mark Hessling, who writes and maintains Regina Rexx and a wide variety of
open source Rexx tools and interfaces. As the technical reviewer for this book, Mark was an invaluable
source of recommendations for improvement as well as (oops!) corrections. His expertise and helpful
ness were critical to improving this book.
Special gratitude is also due to the inventor of Rexx, Michael Cowlishaw. His advice and feedback were
very much appreciated.
In the process of developing this book, I wrote inquiries to many people without any prior introduction.
Each and every one of them responded helpfully. It was a great pleasure to meet people with such an
interest in Rexx, who so kindly answered questions and who greatly improved this book with their
suggestions.
I would like to give heartfelt thanks to Maria Teresa Alonso y Albado, W. David Ashley, Gil Barmwater,
Dr. Dennis Beckley, Alex Brodsky, Frank Clarke, Steve Coalbran, Ian Collier, Les Cottrell, Michael
Cowlishaw, Chip Davis, Prof. Rony Flatscher, Jeff Glatt, Etienne Gloux, Bernard Golden, Bob Hamilton,
Henri Henault , Stephane Henault, Mark Hessling, Jack Hicks, IBM Corporation, Rene Vincent Jansen,
Jaxo Inc., Kare Johansson, Kilowatt Software, Les Koehler, Laboratorios Bago S.A., Joseph A. Latone,
Henri LeFebure, Michael Lueck, Antoni Levato, Dave Martin, Rob McNair, Patrick TJ McPhee, Dr. Laura
Murray, Walter u. Christel Pachl, Lee Peedin, Priscilla Polk, the Rexx Language Association, Pierre G.
Richard, Peggy Robinson, Morris Rosenbaum, Dr. Elizabeth Rovelli, David Ruggles, Roger E. Sanders,
Thomas Schneider, Theresa Stewart, UniForum Chicago, Vasilis Vlachoudis, Stan Wakefield, Keith
Watts, Dr. Sandra Wittstein, and Claudio Zomparelli.
Beyond those who provided technical advice and input for this book, I wish to thank my editors at John
Wiley and Sons, Inc. Eileen Bien Calabro greatly improved the readability of this book through her writ
ing recommendations. Debra Williams Cauley provided insightful perspective and guidance on the
preparation and organization of the book. Finally, I thank Richard Swadley. I appreciate his confidence
and hope this book fulfills its promise both in the quality of its material and in its sales and distribution.
Special thank you to the following developers for permission to reprint or refer to their code (most of
these items fall under various open source licenses):
W. David Ashley—IBM Corporation, project leader of the Mod_Rexx project for scripts appearing in the
chapter on Apache programming with Mod_Rexx
Les Cottrell and the Stanford Linear Accelerator Laboratory—Authors of Rexx/CGI library for a script
illustrating their Rexx/CGI library
Henri Henault & Sons—Authors of the Internet/REXX HHNS WorkBench for a script and screen shot
illustrating the Internet/REXX HHNS WorkBench.
Mark Hessling—Developer/maintainer of Regina Rexx and many open source Rexx tools
for material on Rexx/gd and the reference tables of Rexx/Tk functions
Acknowledgments
Patrick TJ McPhee—Developer of RexxXML for the example program appearing in the chapter on
RexxXML
Pierre G. Richard, Joseph A. Latone, and Jaxo Inc.—Developers of Rexx for Palm OS for example scripts
appearing in the chapter on Rexx for Palm OS
Thank you to Rene Jansen, President of the Rexx Language Association, for his advice and guidance in
putting together this updated edition.
Thank you also to all the members of the RexxLA and others who helped by giving advice or reviewing
this update: Grant Ward Able, Bill Backs, Janet Backs, Volker Bandke, Gil Barmwater, Michael Beer,
Wayne V. Bickerdike, Josep Maria Blasco, Frank Clarke, Michael Cowlishaw, Chip Davis, Marcel Dur,
Lionel B. Dyck, Tony Dycks, Dr Rony Flatscher, Mark L. Gaubatz, Eva Gerger, Sam Golob, Thomas
Grundmann-Kahr, Rob Hamilton, Jeffrey Hennick, Mark Hessling, Peter Jacob, Willy Jensen, Dave
Jones, Per Olov Jonsson, Mark McDonald, Rick McGuire, Shmuel (Seymour J.) Metz, Jeremy Nicoll,
Walter Pachl, Ross Patterson, Lee Peedin, Jochem Peelen, Priscilla Polk, Julian Reindorf, Marc Remes,
Pierre G. Richard, Larry Schacher, Martin Scheffler, Bruce Skelly, David Spiegel, Hobart Spitz, Bob Stark,
Erich Steinbock, Theresa Stewart, J. Leslie Turriff, Vasilis Vlachoudis, James Warren, and Jon Wolfers.
Thank you to those who contributed code for this new edition: Frank Clarke, Marcel Dur, Lionel B. Dyck,
Tony Dycks, Dr Rony Flatscher, Eva Gerger, Thomas Grundmann-Kahr, Mark Hessling, IBM Corporation,
Willy Jensen, Julian Reindorf, Pierre G Richard, and Vasilis Vlachoudis.
Also thank you to Marcel Dur, Eva Gerger, Thomas Grundmann-Kahr, and Julian Reindorf for the very
useful theses they wrote on running Rexx on Android while at the Vienna University of Economics and
Business. Congratulations to Dr Rony Flatscher for his success nourishing such a talented crop of budding
developers and software engineers.
Finally, thank you to Mark Hessling for his help and advice on both editions of this work, and for writing
the Forewords. Mark is the developer behind Regina Rexx and many of the excellent free tools that add key
functionality to Rexx. Several chapters in this book cover products he develops and supports.
And, of course, special thanks to the inventor of Rexx and NetRexx, Michael Cowlishaw.
This book is published under the Creative Commons license BY-ND. You can freely use and distribute
this work, but with these restrictions:
Thank you for respecting my effort in writing this book by your cooperation.
Contents
Foreword ix
Acknowledgments xi
Introduction xv
Part I 1
Chapter 1: Introduction to Scripting and Rexx 3
Chapter 2: Language Basics 21
Chapter 3: Control Structures 33
Chapter 4: Arrays 53
Chapter 5: Input and Output 67
Chapter 6: String Manipulation 79
Chapter 7: Numbers, Calculations, and Conversions 99
Chapter 8: Subroutines, Functions, and Modularity 109
Chapter 9: Debugging and the Trace Facility 133
Chapter 10: Errors and Condition Trapping 143
Chapter 11: The External Data Queue, or “Stack” 159
Chapter 12: Rexx with Style 169
Chapter 13: Writing Portable Rexx 189
Chapter 14: Issuing System Commands 209
Chapter 15: Interfacing to Relational Databases 229
Chapter 16: Graphical User Interfaces 255
Chapter 17: Web Programming with CGI and Apache 273
Chapter 18: XML and Other Interfaces 291
Part II 305
Chapter 19: Evolution and Implementations 307
Chapter 20: Regina 331
Chapter 21: Rexx/imc 345
Chapter 22: BRexx 359
Chapter 23: Reginald 385
Chapter 24: Programming Single Board Computers 421
Chapter 25: Android Programming 433
Chapter 26: r4 and Object-Oriented roo! 447
Chapter 27: Open Object Rexx 459
Chapter 28: Open Object Rexx Tutorial 475
Table of Contents
Index 733
xiv
Introduction
Of all the free scripting languages, why should you learn Rexx? Rexx is unique in that it combines power
with ease of use. Long the dominant scripting language on mainframes, it is definitely a “power”
language, yet it is also so easy to use that its popularity has expanded to every conceivable platform.
Today Rexx developers use the language on Windows, Linux, Unix, BSD, Macs, mainframes and many
dozens of other systems . . . and, there are many free and open source Rexx interpreters available.
You may be wondering why ease of use is so important in a programming language. A truly easy
language is easy to use, learn, remember, and maintain. The benefits to beginners are obvious. With
Rexx, you can start coding almost immediately. There are no syntax tricks or language details to
memorize before you begin. And, since Rexx is also a power language, you can rest assured that you
won’t run out of capability as you learn and grow with it. Read the first few chapters in this book, and
you’ll be scripting right away. Continue reading, and you’ll mature into advanced scripting before you
finish.
If you are an experienced developer, Rexx offers more subtle benefits. You will be more productive, of
course, as you free yourself from the shackles of syntax-driven code. More important is this: simplicity
yields reliability. Your error rate will decline, and you’ll develop more reliable programs. This benefit is
greatest for the largest systems and the most complicated scripts. Your scripts will also live longer because
others will be able to understand, maintain, and enhance them. Your clever scriptlets and application
masterpieces won’t die of neglect when someone else takes over and maintains your code.
Few easy languages are also powerful. Now, how does Rexx do that?
Introduction
Rexx surrounds its small instruction set with an extensive function library. Scripts leverage operating
system commands, external interfaces, programs, and functions. Rexx is a “glue” language that ties it all
together. Yet the language has few rules. Syntax is simple, minimal, flexible. Rexx doesn’t care about
uppercase or lowercase or formatting or spacing. Rexx scripts don’t use special symbols and contain no
punctuation.
If you’ve worked in the shell languages, you’ll breathe a sigh of relief that you’ve found a powerful
language in which you can program now and then without trying to recall arcane language rules. If
you’ve struggled with the syntax of languages such as Bash, Korn, Awk, Perl, C++, or the C-shell, you’ll
enjoy focusing on your programming problem instead of linguistic peculiarities. And if you’ve ever had
to maintain someone else’s code written in one of those languages, well . . . you might really be thankful
for Rexx!
This book contains everything you need to know to get started with Rexx. How to freely download and
install an appropriate interpreter. How to program in standard "classic" Rexx and object-oriented Rexx.
How to program Windows, Linux, Unix, Macs, Androids, and mainframes. How to program in the Java
environment with object-oriented Rexx, or a Rexx-based language called NetRexx. How to script
operating system commands, control Web servers and databases and graphical user interfaces (GUIs)
and Extensible Markup Language (XML) and Apache and . . . you name it.
Everything you need is in this one book. It’s a Rexx encyclopedia. It teaches standard Rexx so that your
skills apply to any platform—from cell phones to desktops and laptops to midrange servers to
mainframes. Yet it goes beyond the basics to cover interface programming and advanced techniques.
The book starts out easy, based on coding examples throughout to make learning fast, simple, and fun.
But it’s comprehensive enough to cover advanced scripting as well. You can freely download all the
Rexx interpreters, tools, and interfaces it covers. Welcome to the world of free Rexx !
□ If you are a complete beginner, you’ll find Rexx easy to learn. This book will tell you
everything you need to know. It’s a progressive tutorial based on coding examples, so you
won't get lost. You’ll be able to handle almost any programming problem by the end of the
book.
□ If you are an experienced programmer, you can quickly learn Rexx by reading the tutorial
chapters. You’ll be programming immediately. As the book progresses into tutorials on
interfaces to databases, Web servers, GUIs, and the like, you’ll learn to program Rexx in the
context of the larger environment.
xvi
Introduction
□ If you are a systems administrator or support person, you’ll learn a language that applies to a
wide variety of situations and can be a great tool. This book covers the interfaces, tools, and
varieties and implementations of Rexx you’ll need to know. It doesn’t stop with the basics. It
explores the advanced features you’ll want to use.
□ If you already script Rexx, you will be able to expand your knowledge. You'll learn about free
Rexx interfaces and tools with which you may not be familiar. You’ll learn Rexx programming
in new environments, such as object-oriented scripting, scripting in the Java environment...
and even how to program Rexx in mainframe emulation on your personal computer. You’ll
find this complete reference the only Rexx book you’ll ever need.
Beyond the Rexx language proper, this book covers all the major interfaces into Web servers, SQL
databases, GUIs, XML, JSON, graphics processing, and the like. It describes many of the free tools that
are available to make scripting with Rexx easier and more productive.
The book covers a number of free Rexx interpreters. Most meet the international standards for Rexx, yet
each adds its own special features and extensions. The book tells where to download each interpreter,
shows how to install it, and demonstrates how to leverage its particular strengths.
All the Rexx interpreters, tools, and interfaces this book covers are free or open source. One exception is
IBM mainframe Rexx, which comes bundled with IBM’s operating systems.
Ultimately, this book covers not only Rexx scripting, but the whole world of Rexx programming across
all environments and interfaces, and with all Rexx interpreters.
□ The book begins with a progressive tutorial that covers the basics of the Rexx language. These
eventually lead into more advanced scripting topics, such as how to write portable code and
using optimal coding style. The last chapters of this section (Chapters 15 through 18) cover the
most common Rexx interfaces and tools. These introduce and demonstrate how to code scripts
that interface to operating systems, SQL databases, Web servers, GUIs, XML, and other tools.
xvii
Introduction
□ The second section of the book describes the different Rexx interpreters and the unique
advantages of each. These chapters apply Rexx to different environments and include tutorials
on object-oriented Rexx, cell phone scripting, programming in Java environments, and many
other topics. All include complete example programs.
□ Finally, the book contains a comprehensive reference section in its appendices. You won’t
need any other book to script Rexx.
Conventions
We’ve used a number of conventions throughout the book.
Boxes like this one hold important, not-to-be-forgotten information that is directly
relevant to the surrounding text.
The gray highlighting is not used for code that’s less important in the present
context, or that has been shown before.
xviii
Introduction
The Rexx language is not case-sensitive, so its instructions and functions can be encoded in uppercase,
lowercase, or mixed case. For example, the wordlength function can be encoded as wordlength,
WordLength, or WORDLENGTH. This book uses capitalization typical to the platforms for which its sample
scripts were written, but you can use any case you prefer.
Due to the typesetting software used in preparing this book, in a few places in the text, quotation
marks might appear as forward-leaning or backward-leaning. Please consider the leftmost
examples as equivalent to the vertical quotation marks that Rexx requires:
Only vertical single or double quotation marks will run correctly when coded in a Rexx program.
Reporting Errors
Thousands of programmers used the first edition of this book and reported no errors beyond a few typos.
Our goal is to match this level of quality in this 2nd edition. This new edition has been reviewed by many
experts from the Rexx Language Association.
However, updating for this new edition required working off the 1st edition's PDF files -- an error-prone
process, especially when working with code in text boxes and frequent font changes. So, try as we might to
catch them, errors introduced by typesetting could slip through.
If you find an error, please contact the author via his website at www.RexxInfo.org and report it. We'll
correct the digital edition immediately, and the print edition as soon as the next printing. Thank you for
your help in making this the best possible resource for Rexx programmers.
Online Resources
The website www.RexxInfo.org is a one-stop shop for all things Rexx. It offers free downloads for all
Rexx interpreters and tools, articles, sample code, information, links, and other resources. Its reference
section offers quick online lookup for all Rexx language instructions, functions, classes, and methods, and
also a full set of free manuals for all Rexx products, including all IBM manuals.
Appendix A on "Resources" describes many other good sources of Rexx information.
xix
Introduction
□ Java integration
□ Using JSON
□ Array I/O
□ Advanced parsing
□ Android programming
□ Rexx on single board computers
□ SQLite programming
□ Equivalence charts for Rexx <--> Bash
□ Equivalence charts for Rexx <--> Python
□ Sample job interview questions
Readers have also asked for more coverage of mainframe Rexx, so we've added:
□ Using EXECIO
□ How to write ISPF edit macros
□ How to run Rexx in batch
□ Rexx on personal computers that emulate mainframes
□ Equivalence charts for IBM mainframe REXX <--> ANSI-1996 standard Rexx
□ Equivalence charts for Rexx <--> Clist
Much new content has been provided in new appendices, rather than chapters, due to typesetting constraints.
We've retained coverage of a few topics that are less important than previously. This includes the
chapters on the out-of-support interpreters Reginald, r4, and roo!, and the brief discussion of
Windows CE. We've kept this content because some readers requested it. It's clearly marked in
the text. If it is not of interest to you, please just skip it.
Among Rexx’s great benefits are its strong language standards and remarkable stability. Thus it hasn’t been
necessary to modify or update the book's code examples. The proof is in the product -- you can rely on Rexx to
run mature code without difficulty or disruption.
Note that this edition does not always reflect the latest names of a few rebranded products. For example, it may
refer to the Db2 database under its older name of "DB2".
And remember that website addresses do change. If one this book suggests becomes obsolete, just use your
search engine to find the material you seek.
xx
Part I
-
Introduction to Scripting
and Rexx
Overview
Before learning the Rexx language, you need to consider the larger picture. What are scripting
languages? When and why are they used? What are Rexx’s unique strengths as a scripting
language, and what kinds of programming problems does it address? Are there any situations
where Rexx would not be the best language choice?
This chapter places Rexx within the larger context of programming technologies. The goal is
to give you the background you need to understand how you can use Rexx to solve the
programming problems you face.
Following this background, the chapter shows you how to download and install the most popular
free Rexx interpreter on your Windows, Linux, Unix, BSD, or Apple computer. Called Regina, this
open-source interpreter provides a basis for your experiments with Rexx as you progress in the
language tutorial of subsequent chapters.
Note that you can use any standard Rexx interpreter to learn Rexx. So, if you have some other
Rexx interpreter available, you are welcome to use it. We show how to download and install
Regina for readers who do not already have a Rexx interpreter installed, or for those who would
like to install an open-source Rexx on their PC.
Why Scripting?
Rexx is a scripting language. What’s that? While most developers would claim to “know one when
they see it,” a precise definition is elusive. Scripting is not a crisply defined discipline but rather
a directional trend in software development. Scripting languages tend to be:
□ Interpreted h Scripting languages do not translate or compile source code into the
computer’s machine code prior to execution. No compile step means quicker
program development.
□ Typeless variables h Powerful scripting languages like Rexx even relieve the programmer of
the burden of declaring data types, defining the kind of data that variables contain. Rexx
understands data by usage. It automatically converts data as necessary to perform
arithmetic operations or comparisons. Much of the housekeeping work programmers
perform in traditional programming languages is automated. This shifts the burden of
programming from the developer to the machine.
Figure 1-1 contrasts scripting languages and more traditional programming languages.
Figure 1-1
On the downside, scripting requires greater machine resources than hand-coded programs in
traditional, compiled languages. But in an era where machine resources are less expensive than ever and
continue to decline in price, trading off expensive developer time for cheaper hardware makes sense.
4
Introduction to Scripting and Rexx
Here’s how hardware addresses scripting performance. The original IBM PC ran an 8088 processor at
4.77 MHz. It executes less than a hundred clauses or statements of a Rexx script every second. Current
personal computers execute millions of Rexx clauses per second.
Just for fun, this table shows how much faster a standard Rexx benchmark script runs on typical PCs
at various times. Later in this chapter, we’ll show you how to benchmark your own computer against
the numbers in this table:
Source- author’s hands-on tests and contributions by members of the Rexx Language Association.
The bottom line is that the program that consumes over an hour on a decades-old 8088 runs in a split
second on a modern desktop. While the table ignores subtle factors that affect performance, the trend
is clear. For most programming projects, trading machine cycles for labor costs makes sense. Why not
use a more productive tool that shifts the burden to the machine?
Labor-saving benefits extend beyond program development to maintenance and enhancement. Experts
like T. Capers Jones estimate that up to 75 percent of IT labor costs are devoted to program maintenance.
An easy-to-read, easy-to-maintain scripting language like Rexx saves a great deal of money.
5
Chapter 1
Sometimes, you’ll hear the claim that scripting languages don’t support the development of large,
robust, “production-grade” applications. Years ago, scripting languages were primitive and this charge
rang true. But no longer. IT organizations routinely develop and run large applications written in Rexx
and other scripting languages. The author has run across many large production business applications
consisting of tens of thousands of lines of code. You can run an entire enterprise on scripts.
Why Rexx?
The distinguishing feature of Rexx is that it combines ease of use with power. Its goal is to make scripting
as easy, fast, reliable, and error-free as possible. Many programming languages are designed for
compatibility with older languages, the personal tastes of their inventors, the convenience of compiler
writers, or machine optimization. Rexx ignores extraneous objectives. It was designed from day one to
be powerful yet easy to use.
One person invented Rexx and guided its development: Michael Cowlishaw of IBM’s UK laboratories.
Cowlishaw gave the language the coherent vision and guiding hand that ambitious software projects
require to succeed. Anticipating how the Internet community would cooperate years later, he posted
Rexx on the internet of its day, IBM’s VNET, a network of tens of thousands of users. Cowlishaw
solicited and responded to thousands of emailed suggestions and recommendations on how people
actually used early Rexx. The feedback enabled Cowlishaw to adapt Rexx to typical human behavior,
making Rexx a truly easy-to-use language.
Ease of use is critical — even to experienced developers — because it leads to these benefits:
□ RedueeS o—t — F program dsi dsvplmnmbnft sosipIed whfi a low error r andthslh Irsll abliabiltty,
lsad to rsducsd coata. Eaas of masntsnancs sa crstscal bscauas upto thrss-quartsra of IT
profsaaionalaIsngagsIinImaintsnancsIactivitisa.ICodsIlrittsnIbyIothsraIiaIsaaisrItoIundsratand
andImaintainIifIitIiaIlrittsnIinIesxxIinatsadIofIayntax-drivsnIlanguagsaIliksIthsILinuxIahsllI
languagsaIorIPsrlIorIAlkIorIC++.IThiaIrsducsaIlaborIcoata.
6
Introduction to Scripting and Rexx
□ yasyeo remember — If you write only theocconiopalpramram,yoe’U free Rexx oaoy to
remember. Languages with special characters and quirky syntax force you to review
tleir rules if oIu Inlo script nIo ind tlen.
□ nsmsfkr sfc—s — Sinee Rexx is o aoy to work wi th,clevplopern fin dit o aoy topd apt topiatform
differences Ir tle requirements If different interfices. Rexx lis i strIng plitfIrm-
kntepentent xtintirt. Ax oell, fino eexx knterpicex int toolx ire tlefxelvex croxx-plitporf
protuctx.
Eate of ute and power traditionallo force language trade-offt. It it eato to get one without the other, but
difficult to achieve both. Rexx it tpecificallo detigned to combine the two. It achievet thit goal through
thete principlet:
□ Siople syntax b Some vero powerful languaget relo extentivelo on tpecial tombolt,
nonobviout default behaviort, default variablet, and other programming thortcutt. But there
it no rule that power can onlo be achieved in thit manner. Rexx etchewt complex “tontax
programming” and encouraget timpler, more readable programming bated on Englith-
language keoword inttructiont and functiont.
□ Small commana set,hvitnfunntionspronidinr thepower — Reha hatm amah eoreof onlyOwodozen
inttructiont. Thit timplicito it turrounded bo the power of tome 70 built-in functiont. A well-
defined, ttandard interface permitt Rexx to call upon external function librariet. Thit allowt
oou to extend the language oourtelf, and it meant that mano open-tource extentiont or
librariet of routinet are freelo available. Rexx tcriptt alto wield the full power of the operating
tottem becaute theo eatilo ittue operating tottem commandt.
□ Foee-inoo lmndumde b Rexx it not cate-tentitive. It it a ioee-inoo lmndumde and it about at
forgiving concerning placement of itt tource text at a programming language can be. Thit
permitt programmert to telf-detcribe programt bo techniquet tuch at indentation, readable
commentt, cate variationt, and the like. Rexx relievet programmert from concern about
tontax and placement, and lett them concentrate on the programming problem theo face.
□ Cnnsisaena, oelimble benmvino b Rexx behavet “at one would attume” at evero opportunito. Itt
earlo uter communito provided feedback to one “matter developer” who altered the
language to conform to topical human behavior. At the inventor ttatet in hit book defining
Rexx: “Tne lmndumde useo is usumlly oidna.” Rexx wat detigned to encourage good programming
practice and then enhanced bo uter feedback to conform to human expectationt.
□ Modulaaitn anO structuredpoogromm—g — Rexxvuraurxgen anclsu pportomoaula nty and
ttructured programming. Breaking up large programming problemt into ditcrete piecet and
rettricting program flow to a tmall tet of language conttructt contributet greatly to eate of
ute and a low error rate when developing large applicationt. Thete principlet oield timplicito
without compromiting power. (Chaptert 3 and 8 explore them.)
7
Chapter 1
The vast majority of Rexx tools are free and open source, too. Hundreds of them cover every
conceivable need, from graphical user interfaces to databases, from telecommunications to
development environments, to almost every other category you can imagine.
Universality
Rexx is a universal language. It runs on every platform, from cell phones and handhelds, to laptops and
desktops, to servers of all kinds, all the way up to the largest mainframes and supercomputers.
Here are the many platforms on which free Rexx interpreters run:
□ All versions of —indows, Linux, Unix, BSD, Apple operating systems and macOS, and all
IBM operating systems.
□ Cell phones and tablets running Android and other various kinds of handhelds
□ Single board computers and embedded systems applications
□ IBM servers (i-series, pSeries, PO—ER/PowerPC, Linux for Z, OpenEdition/Unix System
Services), Amiga-derived systems (AmigaOS 4 or AOS4, MorphOS, AROS, aeROS or
AEROS), DOS-family systems (MS-DOS, PC-DOS, FreeDOS, all others), OS/2-derived systems
(eCS and ArcaOS), plus OpenVMS, QNX, and others
□ Many other lesser-used platforms too numerous to list
8
Introduction to Scripting and Rexx
The benefits of a universal language are several. Among them:
Here’s on example. A site that downsizes its mainframes to Linux servers could install free Rexx on the
L-nux flch-nek. Rexx beclfek the veh-cle tl trlnkfer perklnnel kk-llk, wh-le prlv-d-ng l blke flr
migrating scripts.
A final example: a company runs a data center with mainframes and Unix servers, uses Windows on the
desktop, and programs Android tablets for field agents. Rexx runs on all these platforms, making
developers immediately productive across the whole range of company equipment. Rexx enables a
mainframer to program a handheld, or Windows developer to script under Unix.
A standardized scripting language that is freely available across a wide range of systems yields
unparalleled skills applicability and code portability.
□ As a “glue” language — Rexx has long been used as a high-productivity “glue” language for
stitching together existing commands, programs, and components. Rexx offers a higher-level
interface to underlying system commands and facilities. It leverages services, functions,
objects, widgets, programs, and controls.
□ Automating repetitive tasks — Rexx scripts automate repetitive tasks. You can quickly put
together little scripts to tailor the environment or make your job easier. Rexx makes it easy
to issue commands to the operating system (or other environments or programs) and react
to their return codes and outputs.
□ Extending the operating system — You typically run Rexx scripts simply by typing their name
at the operating system’s command prompt. In writing scripts, you create new operating
system “commands” that extend or customize the operating system or programming
environment.
9
Chapter 1
10
Introduction to Scripting and Rexx
Rexx is not a systems programming language. If you need to code on the machine level, for example, to
write a device driver or other operating system component, Rexx is probably not a good choice. While
there are versions of Rexx that permit direct memory access and other low-level tasks, languages like
C/C++ or assembler are more suitable. Standard Rexx does not manipulate direct or relative addresses,
change specific memory locations, or call PC interrupt vectors or UEFI/BIOS service routines.
Rexx is a great tool to develop clear, readable code. But it cannot force you to do so; it cannot save you
from yourself. Chapter 12 discusses “Rexx with style” and presents simple recommendations for
writing clear, reliable code.
Scripting languages consume more processor cycles and memory than traditional compiled languages.
This affects a few projects. An example is a heavily used transaction in a high-performance online trans
action processing (OLTP) system. The constant execution of the same transaction might make it worth
the labor cost to develop it in a lower-level compiled language to optimize machine efficiency.
A final example might be where you’re writing part of an operating system that is in constant use. In
this case it might be worth investing the extra effort to develop the code in a low-level language like an
assembler, or at least a fast compiled language (like C), to gain maximum efficiency and the quickest
possible execution time.
In most other situations, for most other applications, our profession has reached the consensus that
scripting languages are plenty fast enough. And they are so much more productive! This is why
the shift to scripting has been one of the biggest software trends since the turn of the century.
If you’re interested in reading further about the evolution towards scripting, chapter 19 explores this
in some detail.
Figure 1-2 summarizes the kinds of programming problems to which Rexx is best suited as well as those
for which it may not be the best choice.
11
Chapter 1
Figure 1-2
Which Rexx?
There are many free implementations of what we refer to as standard or classic Rexx. This is Rexx as
defined by the TRL-2 or ANSI standards mentioned earlier. There are also two object-oriented supersets
of classic procedural Rexx. And, there is NetRexx, the free Rexx-like language that runs in a Java Virtual
Machine and presents a complementary or an alternative to Java for developing applications. Which
Rexx should you use?
The first half of this book teaches classic Rexx. It applies to any standard Rexx interpreter on any platform.
Once you know standard Rexx you can easily pick up the extensions unique to any Rexx interpreter.
You can also easily learn interface programming, how to use Rexx tools and packages, object-
oriented Rexx, NetRexx, or any Rexx variant. After all, the whole point of Rexx is ease of learning!
This table summarizes the major Rexx interpreters. All are free and available at no cost (except for IBM's Rexx
Compiler). Most are open source, while a few are proprietary and come bundled with an operating system.
They are distributed either as easy-to-install packages, directly executable binaries, or source code.
12
Introduction to Scripting and Rexx
All these interpreters meet the TRL-2 Rexx language standard. The single exception is NetRexx, which is
best termed a “Rexx-like” language. Any standard Rexx you have installed can be used for working with
the sample code in the first half of this book. This includes all the previously listed interpreters (except
NetRexx), as well as standard Rexx interpreters bundled with mainframe or other operating systems.
To get you up and programming quickly, we defer closer consideration of the unique strengths of the
various Rexx interpreters and the differences between them.
(If you need to know more right now, skip ahead to Chapter 19. That chapter discusses the evolution
of Rexx and the roles it plays as a scripting language. It describes all the free Rexx interpreters above
and presents the strengths of each. Chapters 20 through 30 then show how and where to download
and install each Rexx interpreter. They describe the unique features of each and demonstrate many of
them in sample scripts.)
If you’re new to Rexx, we recommend starting with Regina Rexx. Regina Rexx is a great place to start for
several reasons:
13
Chapter 1
□ Opensourcs — l^na l isaieeree anp ossoi rse rceasddibinei.i tu! un dwe Ge GNULabrary
General Public License. A few neRR interpreters are free but not open source, as shown
in the preceding table.
the code eRamples in this book all conform to standard neRR and were tested using negina neRRunder
Windows and/or LinuR. nun these scripts under any standard neRR in any environment. A few scripts
require a specific operating system. For eRample, those in Chapter 14 illustrate how to issue operating
system commands and therefore are system-specific. Other scripts later in the book use specific open
source interfaces, tools, or interpreters. Where we present eRamples that run only in certain environments,
we’ll point it out.
to get you ready for the rest of the book, the remainder of this chapter shows you how to download
and install negina under Windows, LinuR, GniR, BSD, and macOS. You need only install negina if you
don’t already have access to a neRR interpreter.
14
I ntroduction to Scripting and Rexx
Remember those computer benchmarks on page 5? Let’s benchmark your computer and see how it
compares. This will also verify that Regina is installed and working properly.
15
Chapter 1
Select ReginaRexx from the Windows Program list. Under the ReginaRexx entry, pick Regina Rexx Demos.
Then click on the program Rexxcps. The program will run in a command window and give you a
benchmark you can directly compare to those on page 5.
So, you can run any Rexx program with the file extension of .rexx simply by double-clicking its filename.
Or you can run programs from the command line. Assuming you've navigated to the directory containing
the program you want to run, (or that your PATH environmental variable contains that directory), you can
enter its full filename to run it: rexxcps.rexx
The filename extension of .rexx tells Windows to run the file using Regina Rexx. Or you can run a file
by explicitly invoking the Regina interpreter. Since you've already set the filename extension within
Windows, you can either specify the full or partial filename. Either will work:
What about entering just the filename, but without its extension? Windows can't run this program
because it doesn't know it should invoke Regina to run it. So this will not work: rexxcps
There are several ways to install Regina on Linux and similar computers, such as those running macOS,
Unix, and BSD. Perhaps the quickest and easiest is to use your distribution’s package manager, its software
interface for installing and removing software products.
Example package managers include Ubuntu’s Software Center, the Synaptic Package Manager, APT, DNF,
YUM, and ZYPP. The one you have available depends on which Linux distribution you run. All have easy-
to-use GUI interfaces.
Most Linux package managers connect to large repositories of software products. Just search for Regina in
that list of installable programs, and then select and install Regina using the package manager’s graphical
interface.
In most cases, the package manager will direct you to download two Regina files. One is for the
interpreter itself, and the other is for its library. For example, using Synaptic Package Manager directed
me to download the files regina-rexx, and its library, libregina3. (The exact file names may vary
in your case.) The point is that the package manager will often automatically direct you to download
more than a single file to install Regina. Just download these files and double-click to install them.
If you're on a Mac and use HomeBrew, you can install simply by: brew install regina-rexx
Installing Regina with your operating system’s package manager is quick and easy. But it does have one
possible downside. Sometimes repositories don’t contain the latest release of the software.
16
Introduction to Scripting and Rexx
Thus, you may want to install Regina directly from its permanent project homepage at
SourceForge.net. This is the same SourceForge webpage mentioned in the above discussion on
“Installing Regina on Windows.” The web address for downloading Regina files is:
https://sourceforge.net/projects/regina-rexx/files/.
Once there, you can download the product documentation by selecting the directory regina-
documentation, and the product itself by choosing regina-rexx.
We recommend downloading the two Regina manuals available under the regina-documentation
directory. You may have need of them later.
In the regina-rexx directory, you’ll want to select the current release. There you'll view a very long product
list. Scroll down to the section labelled Linux. This reduces the downloads to a more manageable list.
To find the file to download, scroll through the list under the label Operating Systemand locate yours.
(If you can't find an exact match, pick the one closest to your system. For example, someone running a
Debian-based distribution that is not explicitly listed could use the Debian packages.)
Once you’ve located your operating system, ensure that theArchitecture and 32bit or64bit columns
match your system. The rightmost column will then tell you which kind of package is involved: Debian,
RPM, Alpine, or whatever.
Now you’ll need to download and install two packages. One will be Regina’s library package, while the
second is the interpreter itself.
Here’s an example. At the time of writing, I ran Linux Mint 21.2. I found that Linux Mint did not appear in
the list ofOperating Systems in the Linux section. A quick web search showed that Mint 21.x is based on
Ubuntu 22. So I downloaded the files for Ubuntu 22. They worked fine.
Just as when you install via your Linux package manager, you’ll likely need to install two files. I installed
these two (the underscores contain the variable product release numbers):
As always with package managerfiles, you just double-click to install them. So first I installed the library
.deb, then next, theregina-rexxpackage. Done!
Afterwards, you'll want to test the install to ensure it worked correctly. Let's run the benchmarking
program that produced the table on page 5. You can benchmark your computer against those in the table.
The benchmarking program is called rexxcps. With the default Regina extension of .rexx, that means the
full name of this program is rexxcps.rexx. Use your operating system's Search function to locate this
program. Then open a terminal window, change to the directory where it resides, and execute it:
17
Chapter 1
If Regina can’tfind the program to run it, you can run it by specifying that it resides in the current
directory:
If the simple "package install" we describe above didn't work for you, or if you have an uncommon
system not included in Regina's list of package installs, here we’ll describe a simple, generic approach
that will work for almost any Unix-derived operating system, including the macOS.
Where slight differences exist between systems, the Regina Install Notes will tell you what you need to
know. These typically reside in INSTALL* or README* files. They download with the product. Be sure to
read those instructions!
To install Regina under any Linux, Unix, BSD, or macOS operating system, use the root user ID and
download the source file into an empty directory.
The source file will be compressed. It will thus end in one of thesefile name extensions:
.tar.bz2, .tar.gz, .tgz, .tar, or .zip. To uncompress this file, just double-click on the file name.
If double-clicking produces a second compressed file (such as a .tar file), double-click on that file.
You'll know you're done decompressing when you see a long list of files being created. (So,
decompressing might be either a one-step or two-step process, depending on what kind of compressed
file you initially downloaded.)
Now, open a terminal window and navigate to the home directory into which you extracted Regina.
Look in the directory into which thefiles were extracted andfind and read the Install Notes. They are
usually in a file named INSTALL* or README*. These notes give operating system specific instructions
you must follow to successfully install Regina.
The Install Notes will tell you that you need to enter these two commands as the root user id to the
operating system:
./configure
make install
These commands configure and install Regina. Since they compile source code, they require a C
compiler to run. Almost all Linux, Unix, and BSD machines will have a C compiler present. If your
system does not have one installed, download a free compiler from any of several sites including
www.gnu.org. For macOS, go to the Apple Store and download Xcode.
18
Introduction to Scripting and Rexx
The Install Notes will tell you if you need to set any environmental variables. Typically you'll need to set your
PATH to include the location of the Regina binary. And, you'll need to point the LD_LIBRARY_PATH
environmental variable to where Regina's shared library file resides (usually in /usr/local/lib).
Again, refer to the install instructions! They'll give you the exact commands to run, which do vary by the
operating system.
Finally, test your installation by running one of the Regina-provided demo scripts. Let’s
benchmark your system by running the benchmark program used in the table on page 5 of this
chapter. You can compare your system’s performance to the examples listed in that table.
The program to run is called rexxcps.rexx. You may have to look around in the Regina
install directories to locate it. Change to that directory, then enter this to run the program:
If you have any problems with installing, check that your PATH variable properly includes the path of the
Regina executable, and that the LD_LIBRARY_PATH or its equivalent properly points to Regina's shared
library.
Also, remember that to run a program you may need to set its permission bits as executable:
chmod +x your_program_name.rexx
Start this program from the command line, and you can enter various Rexx statements to it. It responds by
executing the statement and returning its results to you.
Rexxtry can be useful to learn how Rexx works. Here's an example interaction where we test to see if Rexx
treats single and double quotation marks as the same by entering a say instruction:
Rexxtry:
say 'Are single quotes' "the same as double quotes?"
Are single quotes the same as double quotes?
Rexxtry:
exit
19
Chapter 1
Summary
This chapter lists the advantages of scripting in Rexx and suggests where Rexx is most useful. Given its
power, flexibility, portability, and ease of use, Rexx is suitable for addressing a wide range of
programming problems. The only situations where Rexx does not apply are those oriented toward
“systems programming” and programs that demand totally optimized machine utilization.
Rexx distinguishes itself among scripting languages by combining ease of use with power. Rexx uses
specific interpreter design techniques to achieve this combination. Rexx has simple syntax, minimal
"special variables," no "default variables", and a case-insensitive free-format combined with a small, easily
learned instruction set. Its many built-in functions, extensibility, and the ability to issue commands to the
operating system and other external interfaces give Rexx power while retaining ease of use.
Ease of use is important even to highly experienced computer professionals because it reduces error
rates and determines the life span of their code. Experienced developers leverage a quickly coded
language like Rexx to achieve outstanding productivity.
The final part of this chapter showed how to download and install Regina Rexx under Windows, Linux,
Unix, BSD, and macOS. This popular Rexx interpreter is a free, open-source product you can use to
learn Rexx in the tutorial of the following chapters. Any other standard Rexx interpreter could be used
as well. The next several chapters get you quickly up and running Rexx scripts through an example
based tutorial.
20
Language Basics
Overview
This chapter describes the basic elements of Rexx. It discusses the simple components that make
up the language. These include script structure, elements of the language, operators, variables,
and the like. As a starting point, we explore a simple sample script. We’ll walk through this script
and explain what each statement means. Then we’ll describe the language components individu
ally, each in its own section. We’ll discuss Rexx variables, character strings, numbers, operators,
and comparisons.
By the end of this chapter, you’ll know about the basic components of the Rexx language. You’ll be
fully capable of writing simple scripts and will be ready to learn about the language features
explored more fully in subsequent chapters. The chapters that follow present other aspects of the
language, based on sample programs that show its additional features. For example, topics cov
ered in subsequent chapters include directing the logical flow of a script, arrays and tables, input
and output, string manipulation, subroutines and functions, and the like. But now, let’s dive into
our first sample script.
A First Program
Had enough of your job? Maybe it’s time to join the lucky developers who create computer games
for a living! The complete Rexx program that follows is called the Number Game. It generates a
random number between 1 and 10 and asks the user to guess it (well, okay, the playability is a bit
weak...... ) The program reads the number the user guesses and states whether the guess is correct.
the_number = random(1,10)
pull the_guess
say ‘Bye!’
C:\Regina\pgms>number_game.rexx
I’m thinking of number between 1 and 10. What is it?
4
Sorry, my number was: 6
Bye!
C:\Regina\pgms>number_game.rexx
I’m thinking of number between 1 and 10. What is it?
8
You guessed it!
Bye!
This program illustrates several Rexx features. It shows that you document scripts by writing whatever
description you like between the symbols /* and */. Rexx ignores whatever appears between these
comment delimiters. Comments can be isolated on their own lines, as in the sample program, or they can
appear as trailing commentsafter the statement on a line:
Comments can even stretch across multiple lines in box style, as long as they start with /* and end
with */ :
/************************************************************************
* The NUMBER GAME - User tries to guess a number between 1 and 10 *
* Generate a random number between 1 and 10 *
************************************************************************/
Rexx is case-insensitive. Code can be entered in lowercase, uppercase, or mixed case; Rexx doesn’t care.
The if statement could have been written like this if we felt it were clearer:
The variable named the_number could have been coded as the_number or the_number. Since Rexx
ignores case it considers all these as references to the same variable. The one place where case doesmat-
ter is within literalsor hardcoded character strings:
22
Language Basics
while
Character stringsare any set of characters occurring between a matched set of either single quotation
marks (‘) or double quotation marks (“).
What if you want to encode a quote within a literal? In other words, what do you do when you need to
encode a single or double quote as part of the character string itself? To put a single quotation mark
within the literal, enclose the literal with double quotation marks:
To encode double quotation marks within the string, enclose the literal with single quotation marks:
Rexx is a free-format language. The spacing is up to you. Insert (or delete) blank lines for readability, and
leave as much or as little space between instructions and their operands as you like. Rexx leaves the cod
ing style up to you as much as a programming language possibly can.
About the only situation in which spacing is notthe programmer’s option is when encoding a Rexx func
tion. A function is a built-in routine Rexx provides as part of the language; you also may write your own
functions. This program invokes the built-in function random to generate a random number between 1
and 10 (inclusive). The parenthesis containing the function argument(s) must immediately follow the
function name without any intervening space. If the function has no arguments, code it like this:
the_number = random()
Rexx requires that the parentheses occur immediately afterthe function name to recognize the function
properly.
The sample script shows that one does not need to declareor predefine variables in Rexx. This differs
from languages like C++, Java, COBOL, or Pascal. Rexx variables are established at the time of their first
use. The variable the_number is defined during the assignment statement in the example. Space for the
variable the_guess is allocated when the program executes the pull instruction to read the user’s
input:
pull the_guess
In this example, the pull instruction reads the characters that the user types on the keyboard, until he
or she presses the <ENTER> key, into one or more variables and automatically translates them to upper
case. Here the item the user enters is assigned to the newly created variable the_guess.
23
Chapter 2
All variables in Rexx are variable-length character strings. Rexx automatically handles string length
adjustments. It also manages numeric or data type conversions. For example, even though the variables
the_number and the_guess are character strings, if we assume that both contain strings that represent
numbers, one could perform arithmetic or other numeric operations on them:
Rexx automatically handles all the issues surrounding variable declarations, data types, data conver
sions, and variable length character strings that programmers must manually manage in traditional
compiled languages. These features are among those that make it such a productive, high-level
language.
Language Elements
Rexx consists of only two dozen instructions, augmented by the power of some 70 built-in functions.
Figure 2-1 below pictorially represents the key components of Rexx. It shows that the instructions and
functions together compose the core of the language, which is then surrounded and augmented by other
features. Alot of what the first section of this book is about is introducing the various Rexx instructions
and functions.
Elements of Rexx
Of course, this book also provides a language reference section in the appendices, covering these and
other aspects of the language. For example, Appendix B is a reference to all standard Rexx instructions,
while Appendix C provides the reference to standard functions.
24
Language Basics
The first sample program illustrated the use of the instructions say, pull, and if. Rexx instructions are
typically followed by one or more operands, or elements upon which they operate. For example, say is
followed by one or more elements it writes to the display screen. The pull instruction is followed by a
list of the data elements it reads.
The sample script illustrated one function, random. Functions are always immediately followed by
parentheses, usually containing function arguments, or inputs to the function. If there are no arguments,
the function must be immediately followed by empty parentheses () . Rexx functions always return a
single result, which is then substituted into the expression directly in place of the function call. For
example, the random number returned by the random function is actually substituted into the statement
that follows, on the right-hand side of the equals sign, then assigned to the variable the_number:
the_number = random(1,10)
Variablesare named storage locations that can contain values. They do not need to be declared or defined
in advance, but are rather created when they are first referenced. You can declare or define all variables
used in a program at the beginning of the script, but Rexx does not require this. Some programmers like
to declare all variables at the top of their programs, for clarity, but Rexx leaves the decision whether or
not to do this up to you.
All variables in Rexx are internally stored as variable-length strings. The interpreter manages their
lengths and data types. Rexx variables are “typeless” in that their contents define their usage. If strings
contain digits, you can apply numeric operations to them. If they do not contain strings representing
numeric values, numeric operations don’t make sense and will fail if attempted. Rexx is simpler than
other programming languages in that developers do not have to concern themselves with data types.
Variable namesare sometimes referred to as symbols. They may be composed of letters, digits, and charac
ters such as . ! ? _ . A variable name you create must not begin with a digit or period. Asimple variable
namedoes not include a period. A variable name that includes a period is called a compound variableand
represents an arrayor table. Arrays will be covered in Chapter 4. They consist of groups of similar data
elements, typically processed as a group.
If all Rexx variables are typeless, how does one create a numeric value? Just place a string representing a
valid number into a Rexx variable. Here are assignment statementsthat achieve this:
whole_number_example = 15
decimal_example = 14.2
negative_number = -21.2
exponential_notation_example = 14E+12
Anumberin Rexx is simply a string of one or more digits with one optional decimal point anywhere in
the string. Numbers may optionally be preceded by their sign, indicating a postive or a negative num
ber. Numbers may be represented very flexibly by almost any common notation. Exponential numbers
may be represented in either engineering or scientific notation (the default is scientific). The following
table shows examples of numbers in Rexx.
25
Chapter 2
Variables are assigned values through either assignment statements or input instructions. The assign
ment statement uses the equals sign (=) to assign a value to a variable, as shown earlier. The input
instructions are the pull or parse instructions, which read input values, and the arg and parse arg
instructions, which read command line parameters or input arguments to a script.
If a variable has not yet been assigned a value, it is referred to as uninitialized. The value of an uninitial
ized variable is the name of the variable itself in uppercase letters. This if statement uses this fact to
determine if the variable no_value_yet is uninitialized:
Character stringsorliteralsare any set of characters enclosed in single or double quotation marks ( ‘ or “ ).
If you need to include either the single or double quote within the literal, simply enclose that literal with
the other string delimiter. Or you can encode two single or double quotation marks back to back, and
Rexx understands that this means that one quote is to be contained within the literal (it knows the dou
bled quote does not terminate the literal). Here are a few examples:
In addition to supporting any typical numeric or string representation, Rexx also supportshexadecimalor
base 16 numbers. Hex stringscontain the upper- or lowercase letters A through F and the digits 0 through
9, and are followed by an upper- or lowercase X:
Rexx also supports binary, or base two strings. Binary strings consist only of 0s and 1s. They are denoted
by their following upper- or lowercase B:
example_binary_string = ‘10001011’b
another_binary_string = ‘1011’B
Rexx has a full complement of functions to convert between regular character strings and hex and binary
strings. Do not be concerned if you are not familiar with the uses of these kinds of strings in program
ming languages. We mention them only for programmers who require them. Future chapters will
explain their use more fully and provide illustrative examples.
26
Language Basics
Operators
Every programming language has operators, symbols that indicate arithmetic operations or dictate that
comparisons must be performed. Operators are used in calculations and in assigning values to variables,
for example. Rexx supports a full set of operators for the following.
□ Arithmetic
□ Cpmporison
□ Logicaloporators
□ Characterrtring concatenation
+ Addition
- Subtraction
* Multiplication
/ Division
% Inteter division—returns the inteter part of the result from division
// Remainder division—returns the remainder from division
** Raise to a whole number power
+ (as a prefix ) Indicates a positive number
- (as a prefix) Indicates a netative number
All toithrrtii oarottoot wook tt orr would ttturr foor bttii hith-tihool tltrbot, oo foor aoototr-
mint in most other common protrammint lantuates. Here are a few examples usint the less obvious
operators:
Remember that because all Rexx variables are strints, arithmetic operators should only be applied to
variables that evaluate to valid numbers. Apply them only to strints containint ditits, with their
optional decimal points and leadint sitns, or to numbers in exponential forms.
Numeric operations are a major topic in Rexx (as in any protrammint lantuate). The underlyint princi
ple is this—the Rexx standard ensures that the same calculation will yield the same results even when run under
different Rexx implementations or on different computers. Rexx provides an exceptional level of machine- and
implementation-independence compared with many other protrammint lantuates.
27
Chapter 2
If you are familiar with other programming languages, you might wonder how Rexx achieves this
benefit. Internally, Rexx employs decimal arithmetic. It does not suffer from the approximations caused
by languages that rely on floating point calculations or binary arithmetic.
The only arithmetic errors Rexx gives are overflow (or underflow). These result from insufficient storage to
hold exceptionally large results.
To control the number of significant digits in arithmetic results, use the numeric instruction.
Sometimes the number of significant digits is referred to as the precision of the result. Numeric precision
defaults to nine digits. This sample statement illustrates the default precision because it displays nine
digits to the right of the decimal place in its result:
This example shows how to change the precision in a calculation. Set the numeric precision to 12 digits
by the numeric instruction, and you get this result:
Chapter 7 explores computation further. It tells you everything you need to know about how to express
numbers in Rexx, conversion between numeric and other formats, and how to obtain and display
numeric results. We’ll defer further discussion on numbers and calculations to Chapter 7.
Comparison operators provide for numeric and string comparisons. These are the operators you use to
determine the equality or inequality of data elements. Use them to determine if one data item is greater
than another or if two variables contain equal values.
Since every Rexx variable contains a character string, you might wonder how Rexx decides to perform a
character or numeric comparison. The key rule is: if both terms involved in a comparison are numeric, then
the comparison is numeric. For a numeric comparison, any leading zeroes are ignored and the numeric
values are compared. This is just as one would expect.
If either term in a comparison is other than numeric, then a string comparison occurs. The rule for string
comparison is that leading and trailing blanks are ignored, and if one string is shorter than the other, it
is padded with trailing blanks. Then a character-by-character comparison occurs. String comparison is
case-sensitive. The character string ABC is not equal to the string Abc. Again, this is what one would
normally assume.
Rexx features a typical set of comparison operators, as shown in the following table:
28
Language Basics
= Equal
\= -= Not equal
> Greater than
< Less than
>= \< -< Greater than or equal to, not less than
<= \> -> Less than or equal to, not greater than
>< <> Greater than or less than (same as not equal)
The “not” symbol for operators is typically written as a backslash, as in “not equal:” \= But some
times you’ll see it written as - as in “not equal:” -= Both codings are equivalent in Rexx. The first repre
sentation is very common, while the second is almost exclusively associated with mainframe scripting.
Since most keyboards outside of mainframe environments do not include the symbol we recommend always
using the backslash. This is universal and your code will run on any platform. The backslash is the ANSI
standard Rexx symbol. You can also code “not equal to” as: <> or >< .
Rexx also provides for strict comparisonsof character strings. In strict comparisons, two strings must be iden
tical to be considered equal—leading and trailing blanks count and no padding occurs to the shorter
string. Strict comparisons only make sense in string comparisons, not numeric comparisons. Strict com
parison operators are easily identified because they contain doubled operators, as shown in the follow
ing chart:
== Strictly equal
\== -== Strictly not equal
>> Strictly greater than
<< Strictly less than
>>= \<< -<< Strictly greater than or equal to, strictly not less than
<<= \>> ->> Strictly less than or equal to, strictly not greater than
29
Chapter 2
Here are sample strict string comparisons:
Logical operatorsare sometimes called Boolean operatorsbecause they apply Boolean logic to the operands.
Rexx’s logical operators are the same as the logical operators of many other programming languages.
This table lists the logical operators:
Boolean logic is useful in if statements with multiple comparisons. These are also referred to as com
pound comparisons. Here are some examples:
Concatenationis the process of pasting two or more character strings together. Strings are appended one
to the end of the other. Explicitly concatenate strings by coding the concatenation operator || . Rexx also
automatically concatenates strings when they appear together in the same statement. Look at these
instructions executed in sequence:
The second say instruction shows concatenation through abuttal. A literal string and a variable appear
immediately adjacent to one another, so Rexx concatenates them without any intervening blank.
30
Language Basics
Contrast this to the last say instruction, where Rexx concatenates the literal and variable contents, but
with one blank between them. If there are one or more spaces between the two elements listed as
operands to the say instruction, Rexx places exactly one blank between them after concatenation.
Given these three methods of concatenating strings, individual programmers have their own prefer
ences. Using the concatenation operator makes the process more explicit, but it also results in longer
statements to build the result string.
Rexx has four kinds of operators: arithmetic, comparison, logical, and concatenation. And there are sev
eral operators in each group. If you build a statement with multiple operators, how does Rexx decide
which operations to execute first? The order can be important. For example:
Perform those same operations with the same numbers in a different order, and you get a different result:
Both these computations involve the same two operations with the same three numbers but the opera
tions occur in different orders. They yield different results.
Clearly, programmers need to know in what order a series of operations will be executed. This issue is
often referred to as the operator order of precedence. The order of precedence is a rule that defines which
operations are executed in what order.
Some programming languages have intricate or odd orders of precedence. Rexx makes it easy. Its order
of precedence is the same as in conventional algebra and the majority of programming languages. (The
only minor exception is that the prefix minus operatoralways has higher priority than the exponential
operator).
□ PxeOixoperators + - \
□ ower operator **
□L egical OR |
□E XCLUSIVE OR &&
If the order of precedence is important to some logic in your program, an easy way to ensure that opera
tions occur in the manner in which you expect is to simply enclose the operations to perform first in
parentheses. When Rexx encounters parentheses, it evaluates the entire expression when that term is
required. So, you can use parentheses to guarantee any order of evaluation you require. The more
deeply nested a set of parentheses is, the higher its order of precedence. The basic rule is this: when Rexx
encounters expressions nested within parentheses, it works from the innermost to the outermost.
31
Chapter 2
To return to the earlier example, one can easily ensure the proper order of operations by enclosing the
highest order operations in parentheses:
say (4 * 3) - 2 /* displays: 10 */
To alter the order in which operations occur, just reposition the parentheses:
say 4 * (3 - 2) /* displays: 4 */
Summary
This chapter briefly summarizes the basic elements of Rexx. We’ve kept the discussion high level and
have avoided strict “textbook definitions.” We discussed variable names and how to form them, and the
difference between simple variable names and the compound variable names that are used to represent
tables or arrays. We discussed the difference between strings and numbers and how to assign both to
variables.
We also listed and discussed the operators used to represent arithmetic, comparison, logical, and string
operations. We gave a few simple examples of how the operators are used; you’ll see many more, real-
world examples in the sample scripts in the upcoming chapters.
The upcoming chapters round out your knowledge of the language and focus in more detail on its capa
bilities. They also provide many more programming examples. Their sample scripts use the language
elements this chapter introduces in many different contexts, so you’ll get a much better feel for how they
are used in actual programming.
32
Control Structures
Overview
Program logic is directed by what are called control structures or constructs — statements like
if- then-else, do-while, and the like. Rexx offers a complete set of control structures in less than
a dozen instructions.
Rexx fully supports structured programming, a rigorous methodology for program development
that simplifies code and reduces programmer error. Invented and defined by such experts as
Edsger Dijkstra and Edward Yourdon, structured programming restricts control structures to a
handful that permit single points of entry and exit to code blocks. The “golden rule” of structured
programming mandates that there should be only a single entry point and a single exit point from
any code block, such as a do loop. Goto’s and unstructured entries and exits are prohibited.
Structured programming encourages modularity and reduces complex spaghetti code to short,
readable, sections of self-contained code. Small, well-documented routines mean greater clarity
and fewer programmer errors. While developer convenience sometimes leads to unstructured
code (“Well... it made sense when I wrote it!”), structured, modular code is more readable and
maintainable.
We recommend structured programming; nearly all of the examples in this book are
structured. But we note that, as a powerful programming language, Rexx includes instructions
that permit unstructured coding if desired.
This chapter discusses how to write structured programs with Rexx. We start by listing the Rexx
instructions used to implement structured constructs. Then, we describe each in turn, showing
how it is used in the language through numerous code snippets. At appropriate intervals, we pre
sent complete sample scripts that illustrate the use of the instructions in structured coding.
The latter part of the chapter covers the Rexx instructions for unstructured programming. While
we don’t recommend their general use, there are special situations in which these instructions are
highly convenient. Any full-power scripting language requires a full set of instructions for control
ling logical flow, including those that are unstructured.
Chapter 3
PROCESS Any set of instructions, executed one after another. The exit or
return instructions end the code composing a program or routine.
IF-THEN. IF-THEN-ELSE if
DO. DO-WHILE do
CASE select
CALL call
34
Control Structures
IF Statements
if statements express conditional logic. Depending on the evaluation of some condition, a different
branch of program logic executes. if statements are common to nearly all programming languages, and
they represent the basic structured instruction for conditional logic. The two basic formats of the Rexx if
instruction are:
and
Rexx evaluates the expressionto 1 if it is true, and 0 if it is false. Here are sample if statements:
if datatype(input,N) then
say ‘Your input was a number’
else
say ‘Your input was not numeric’
if (var) then
say ‘VAR evaluated to 1’
else
say ‘VAR evaluated to 0’
To execute more than a single instruction after either the then or else keywords, you mustinsert the
multiple instructions between the keywords do and end. Here is an example:
35
Chapter 3
if datatype(input,N) then do
say ‘The input was a number’
status_record = ‘VALID’
end
else do
say ‘The input was NOT a number’
status_record = ‘INVALID’
end
The do-end pair groups multiple instructions. This is required when you encode more than one instruc
tion as a logic branch in an if instruction. Notice that you must use the do-end pair for either branch of
the if instruction when it executes more than a single statement. In other words, use the do-end pair to
group more than a single instruction on either the then or the else branches of the if instruction.
You can nest if statements, one inside of another. If you nest if statements very deeply, it becomes con
fusing as to which else clause matches which if instruction. The important rule to remember is that an else
clause is always matched to the nearest unmatched if. Rexx ignores indentation, so how you indent nested if
statements has no effect on how Rexx interprets them.
The following code includes comments that show where the programmer sees the end of each if
instruction. He or she includes these for documentation purposes only, since Rexx ignores comments
(regardless of what the comments may say).
Here’s another style in which to code this example. This series of nested if statements is sometimes
referred to as anif-else-if ladder. The first logic branch that evaluates to true executes:
Some languages provide special keywords for this situation, but Rexx does not. (For example, some
Unix shell languages provide the elif keyword to represent Rexx’s else if pair). Remember to code a
do - end pair whenever more than one instruction executes within a branch of the if instruction.
36
Control Structures
The if-else-if ladder embodies another structured construct often referred to the CASE construct. In a
CASE construct, a set of conditions are tested, then one logic branch is selected from among several.
Rexx provides the select instruction to create CASE logic, as will be explained later. In Rexx you can
either choose an if-else-if ladder or the select instruction to encode CASE logic.
Sometimes, you’ll encounter a coding situation where you want to code a logic branch that performs no
action. In this case, code the Rexx nop instruction. “nop” is a traditional computer science abbreviation
or term that means “no operation.” The nop instruction is a placeholder that results in no action. Here
is an example. The nop instruction in this code ensures that no action is taken when the if statement
condition evaluates to true:
DO Statements
The do instruction groups statements together and optionally executes them repetitively. It comes in sev
eral forms, all of which we’ll explore in this chapter. do instructions permit repetitive execution of one or
more statements. They are the basic way you code program “loops” in Rexx.
You are already familiar with the simple do-end pair used to group multiple instructions. Here is the
generic representation of how this is coded:
DO
instruction_list
END
Use the do-end group when you must execute multiple instructions in a branch of the if instruction, for
example. Here’s another form of the do instruction that repetitively executes a group of instructions
while the condition in the expression is true:
DO WHILE expression
instruction_list
END
This coding example shows how to use this generic format. It employs a do while to call a subroutine
exactly 10 times:
j = 1
do while j <= 10
call sub_routine
j = j + 1
end
37
Chapter 3
The do instruction is flexible and offers other formats for devising loops. The preceding loop could also
be coded with a simpler form of the do instruction:
do 10
call sub_routine
end
do j = 1 to 10 by 1
call sub_routine
end
The phrase by 1 is unnecessary because Rexx automatically increases the do loop control variableby 1 if
this phrase is not coded. But the keyword by could be useful in situations where you want to increase
theloop counterby some other value:
do j = 1 to 20 by 2
call sub_routine
end
In addition to the to and by keywords, for may be used establish another limit on the loop’s execution
if some other condition does not terminate it first. for is like to, in that Rexx checks it prior to each iter
ation through the loop. to, by, and for may be coded in any order. In this example, the for keyword
limits the do loop to three executions:
do j = 1 to 100 by 1 for 3
say ‘Loop executed:’ j ‘times.’ /* Ends with: ‘Loop executed: 3 times.’ */
end
You may alter the loop control variable yourself, directly, while inside of the do loop, but this is not a rec
ommended programming practice. It is confusing, and there is always an alternative way to handle such
a situation from the logical standpoint. We recommend always using the loop control variable only for
controlling an individual loop, and only altering that variable’s value through the do instruction condi
tion test.
Rexx also contains unstructured loop control instructions such as leave, iterate, and signal, which
we cover later in the section of this chapter on unstructured control constructs. At that time we also
cover the do until and do forever forms of do loops, which also fall outside the rules of structured
programming.
A Sample Program
This program prompts the user to input a series of words, one at a time. The program identifies words
that are four characters long, and concatenates them into a list, which it then displays. The program
illustrates a basic do loop, using it to read input from the user. It also shows how to use the if instruc
tion in determining the lengths of the words the user enters.
38
Control Structures
If you were to enter this sentence to the program (one word at a time):
now is the time for all good men to come to the aid of their country
The do while loop in this script provides the control structure for the program to prompt the user and
read one word after that prompt. The do while loop terminates when the user declines to enter a word —
after the user just presses the <enter> key in response to the program’s prompt to Enter a word:
When the user presses the <enter> key without entering a word, this statement recognizes that fact and
terminates the do while loop:
do while wordin \=
Recall that the pull instruction reads an input and automatically translates it to uppercase. This pro
gram uses parse pull to read an input withoutthe automatic translation to uppercase:
The period ensures that only the first word is accepted should the user enter more than one. This use of
the period is a convention in Rexx, and it’s about the only example of syntax-based codingin the entire
language. You could achieve the same effect by coding:
The first word entered by the user is parsed into the variable wordin, while any remaining words
entered on the input line would be placed into the variable named junk.
39
Chapter 3
The program uses the length function to determine whether the word the user entered contains four
letters. If so, the next statement concatenates the four letter word into a list it builds in the variable
named four_letter_words.
if length(wordin) = 4 then
four_letter_words = four_letter_words wordin
The assignment statement relies on the fact that Rexx automatically concatenates variables placed in the
same statement, with one space between each. An alternative would have been to use the explicit con
catenation operator:
Explicit concatenation requires explicitly splicing in a blank to achieve properly spaced output:
After the user is done entering words, the program displays the output string through the following
statement. Since this is the last statement coded in the program, the script terminates after issuing it:
SELECT Statements
The CASE construct tests a series of conditions and executes the set of instructions for the first condition
that is true. Rexx implements the CASE construct through its select instruction. The select instruc
tion tests expressions and executes the logic branch of the first one that evaluates to true. Here is the
generic format of the select instruction:
The otherwise branch of the select instruction executes if none of the prior when_list conditions are
found to be true. Note that it is possible to code a select instruction without an otherwise keyword,
but if none of the when_list conditions execute, an error results. We strongly recommend coding an
otherwise section on every select statement.
The Rexx select instruction provides more control than the same CASE construct in some other pro
gramming languages because you can encode any expression in the when clause. Some languages only
permit testing the value of a specified variable.
select
when gender = ‘M’ then
say ‘Gender is male’
40
Control Structures
when gender = ‘F’ then do
say ‘Gender is female’
female_count = female_count + 1
end
otherwise
say ‘Error -- Gender is missing or invalid’
say ‘Please check input record’
end /* this END pairs with the SELECT instruction itself */
If the value in the variable gender equals the character M, the first logic branch executes. If the value is F,
the group of instructions associated with the second when clause runs. If neither case is true, then the
instructions following the otherwise keyword execute.
Notice that an instruction_list follows the otherwise keyword, so if you code more than one state
ment here you do not need to insert them in a do-end pair. Contrast this to the when groups, which do
require a do-end pair if they contain more than a single instruction. Don’t forget to encode the final end
keyword to terminate the select statement.
CALL Statements
All programming languages provide a mechanism to invoke other scripts or routines. This allows one
script, referred to as the caller, to run another, the subroutine. Rexx’s call instruction invokes a subrou
tine, where the subroutine may be one of three kinds:
The subroutine may optionally return one value to the caller through the Reee special variable named
resulta (teee hai only a handnxl on ipecial variablei and result ii one on ehem)a On coxrie, yox can
have the iubroutine iend back one or more reiulti by changing the valuei on variablei it hai acceii toa
We’ll eeplore all the wayi in which caller and iubroutinei or nunctioni can communicate in detail in
Chapter 8, which ii on iubroutinei and modularitya For now, we’ll juit nocui our diicuiiion on the call
initructiona
Subroutinesand functionsare very iimilar in teeea The one dinnerence ii that a nunction mustreturn a
value to the caller by iti return initruction, where a iubroutine may elect do ioa
The nollowing iample program illuitratei the call initruction by invoking an internal routine ai a iub-
routinea The iubroutine ii coniidered internalbecauie iti code reiidei in the iame nile ai that on the pro
gram that calli ita The program iubroutine iquarei a number and returni the reiulta
The main program readi one input number ai a command-line argumentor input parametera To run the pro
gram and get the square ot tour, tor example, you enter this line to specify the command-line argument:
square.rexx 4
41
Chapter 3
Or, you may start the program by entering a line like this:
regina square 4
Recall that the first example given earlier implicitlyinvokes the Rexx interpreter, while the second exam
ple explicitlyinvokes it. The command-line argument follows the name of the Rexx script you want to
run. Here it’s a single value, 4, but other programs might have either many or no command-line
arguments.
/* SQUARE: */
/* */
/* Squares a number by calling an internal subroutine */
exit 0
/* SQUARE_THE_NUMBER: */
/* */
/* Squares the number
num and RETURNs it into RESULT */
square_the_number: procedure
arg the_number
return the_number * the_number
The main program or driveruses the arg instruction to read the command-line argument into variable
number_in. As with the pull and parse pull instructions, encode a period (.) at the end of this state
ment to eliminate any extraneous input:
The call instruction names the internal routine to invoke and passes the variable number_in to that
routine as its input. The subroutine uses the arg instruction to read this parameter (exactly as the main
routine did). Here is the encoding of the call instruction. The first parameter names the subroutine or
function to run, while each subsequent parameter is an input argument sent to the subroutine. In this
case, the call instruction passes a single argument named number_in to the subroutine named
square_the_number:
The first line of the subroutine identifies it as the routine named square_the_number. Notice that a
colon follows its name on the first line of the subroutine—this identifies a labelin Rexx. An internal
42
Control Structures
subroutine starts with the routine’s name in the form of a label. The procedure instruction on the first
line of the subroutine ensures that only the arguments passed to the subroutine will be accessable from
within it. No other variables of the calling routine are viewable or changeable by this subroutine. Here is
the first executable line of the subroutine:
square_the_number: procedure
The subroutine reads the number passed into it from its caller by the arg instruction. Then, the subrou
tine returns a single result through its return instruction. Here is how this line is encoded. Notice that
Rexx evaluates the expression (squaring the number) before executing the return instruction:
The caller picks up this returned value through the special variablenamed result. The main routine dis
plays the squared result to the user through this concatenated display statement:
The driver ends with the instruction exit 0. This unconditionally ends the script with a return code, or
returned value, of 0. The last statement of the internal subroutine was a return instruction. return
passes control back to the calling routine, in this case passing back the squared number. If the subroutine
is a function, a return instruction is required to pass back a value.
There is much more to say about subroutines and modular program design. We leave that discussion to
Chapter 8. For now, this simple script illustrates the structured CALL construct and how it can be used
to invoke a subroutine or function.
The basic idea of the program is that it displays a menu of transaction options to the user. The user picks
which transaction to execute. The program then executes that transaction and returns to the user with
the menu. Here is how it starts. The program clears the screen and displays a menu of options to the
user that looks like this:
Insert = I
Update = U
Delete = D
Exit = X
43
Chapter 3
Based on the user’s input, the program then calls the appropriate internal subroutine to perform an
Insert, Update, or Delete transaction. (In the example, these routines are “dummied out” and all each
really does is display a message that the subroutine was entered). The menu reappears until the user
finally exits by entering the menu option ‘x’.
/* MENU: */
/* */
/* This program display a menu and performs updates based */
/* on the transaction the user selects. */
44
Control Structures
When the Rexx interpreter does not recognize a statement as part of the Rexx language, it assumes that it
is an operating system command and passes it to the operating system for execution. Since there is no
such command as cls in the Rexx language, the interpreter passes the string cls to the operating sys
tem for execution as an operating system command.
cls is the Windows command to “clear the screen,” so what this statement does is send a command to
Windows to clear the display screen. Of course, this statement makes this program operating-system
dependent. To run this program under Linux or Unix, this statement should contain the equivalent com
mand to clear the screen under these operating systems, which is clear:
Passing commands to the operating system (or other external environments) is an important Rexx fea
ture. It provides a lot of power and, as you can see, is very easy to code. Chapter 14 covers this topic in
detail.
Next in the program, a series of say commands paints the menu on the user’s screen:
say
say ‘Select the transation type by abbreviation: ’
say
say ‘ Insert = I ‘
say ‘ Update = U ‘
say ‘ Delete = D ‘
say ‘ Exit = X ‘
say
say ‘Your choice => ‘
A say instruction with no operand just displays a blank line and can be used for vertically spacing the
output on the user’s display screen.
The script displays the menu repeatedly until the user finally enters ‘x’ or ‘X’. The pull command’s
automatic translation of user input to uppercase is handy here and eliminates the need for the program
mer to worry about the case in which the user enters a letter.
The select construct leads to a call of the proper internal routine to handle the transaction the user
selects:
45
Chapter 3
select
when tran_type = ‘I’ then
call insert_routine
when tran_type = ‘U’ then
call update_routine
when tran_type = ‘D’ then
call delete_routine
when tran_type = ‘X’ then do
say
say ‘Bye!’
end
otherwise
say
say ‘You entered invalid transaction type:’ tran_type
say ‘Press <ENTER> to reenter the transaction type.’
pull .
end
The when clause where the user enters ‘x’ or ‘X’ encloses its multiple instructions within a do-end pair.
The otherwise clause handles the case where the user inputs an invalid character. The final end in the
code concludes the select instruction.
Remember that the logic of the select statement is that the first condition that evaluates to true is the
branch that executes. In the preceding code, this means that the program will call the proper subroutine
based on the transaction code the user enters.
Following the select instruction, the code for the main routine or driver ends with an exit 0 statement:
exit 0
This delimits the code of the main routine from that of the routines that follow it and also sends a return
code of 0 to the environment when the script ends. An exit instruction is required to separate the code
of the main routine from the subroutines or functions that follow it.
The three update routines contain no real code. Each just displays a message that it ran. This allows the
user to verify that the script is working. These subroutines cannot access any variables within the main
routine, because they have the procedure instruction, and no variables are passed into them. Each ends
with a return 0 instruction:
return 0
While this sample script is simple, it shows how to code a menu for user selection. It also illustrates call
ing subroutines to perform tasks. This is a nice modular structure that you can expand when coding
menus with pick lists. Of course, many programs require graphical user interfaces, or GUIs. There are a
variety of free and open-source GUI interfaces available for Rexx scripting. GUI programming is an
advanced topic we’ll get to in a bit. Chapter 16 shows how to program GUIs with Rexx scripts.
46
Control Structures
Instruction Use
do until A form of the do instruction that implements a bottom-drive loop. Unlike do-
while, do-until will always execute the code in the loop at least one time,
because the condition test occurs at the bottom of the loop.
do forever Creates an endless loop, a loop that executes forever. This requires an unstruc
tured exit to terminate the loop. Code the unstructured exit by either the
leave, signal or exit instruction.
iterate Causes control to be passed from the current statement in the do loop to the
bottom of the loop.
leave Causes an immediate exit from a do loop to the statement following the loop.
signal Used to trap exceptions(specific program error conditions). Can also be used to
unconditionally transfer control to a specified label, similarly to the goto
instruction in other programming languages.
The do until and do forever are two more forms of the do instruction. do until implements a
bottom-driven loop.Such a loop always executes at least one time. In contrast, the do while checks the
condition priorto entering the loop, so the loop may or may not be executed at least once. do until is
considered unstructured and the do while is preferred. Any logic that can be encoded using do until
can be coded using do while—you just have to think for a moment to see how to change the logic into
a do while.
Let’s look at the difference between do while and do until. This code will not enter the do loop to
display the message. The do while tests the condition prior to executing the loop, so the loop never
executes. The result in this example is that the say instruction never executes and does not display the
message:
ex = ‘NO’
do while ex = ‘YES’
say ‘Loop 1 was entered’ /* This line is never displayed. */
ex = ‘YES’
end
47
Chapter 3
Figure 3-2
If we replace the do while loop with a do until loop, the code will execute through the loop one time,
printing the message once. This is because the condition test is applied only at the bottom of the loop. A
do until loop will always execute one time, even if the condition test on the do until is false, because
the test is not evaluated until after the loop executes one time. The result in this example is that the say
instruction executes once and displays one output line:
ex = ‘NO’
do until ex = ‘YES’
say ‘Loop 2 was entered’ /* This line is displayed one time. */
ex = ‘YES’
end
do forever creates anendless loop. You musthave some unstructured exit from within the loop or your
program will never stop! This example uses the leave instruction to exit the endless loop when j = 4.
The leave instruction transfers control to the statement immediately following the end that terminates
the do loop. In this example, it breaks the endless loop and transfers control to the say statement imme
diately following the loop:
j = 1
do forever
/* do some work here */
j = j + 1
if j = 4 then leave /* exits the DO FOREVER loop */
48
Control Structures
end
say ‘The above LEAVE instruction transfers control to this statement’
Another way to terminate the endless loop is to encode the exit instruction. exit ends a program
unconditionally (even if a subroutine is executing or if execution is nested inside of a do loop). Control
returns to the environment (the operating system) with the optional string encoded on the exit state
ment passed up.
What return code you can pass to the environment or operating system using the exit instruction
depends on what that operating system accepts. Some systems accept only return codes that are numeric
digits between 0 and 127. If your script returns any other string, it is translated into a 0. Other operating
systems will accept whatever value you encode on the exit instruction.
Here’s an example. The following code snippet is the same as the previous one, which illustrates the
leave instruction, but this time when the condition j = 4 is attained, the script unconditionally exits
and returns 0 to the environment. Since the script ends, the say instruction following the do forever
loop never executes and does not display its output:
j = 1
do forever
/* do some work here */
j = j + 1
if j = 4 then
exit 0 /* unconditionally exits and passes ‘0’ back to the environment */
end
say ‘this line will never be displayed’ /* code EXITs, never reaches this line
*/
Another instruction for the unstructured transfer of control is signal. The signal instruction acts much
like the goto statement of other programming languages. It transfers control directly out of any loop,
CASE structure, or if statement directly to a Rexx label. Alabelis simply a symbol immediately fol
lowed by a colon. This sample code snippet is similar to that we’ve seen earlier, except that this time the
signal instruction transfers control to a program label. So, once j = 4 and the signal instruction exe
cute, control is transferred to the program label and the say instruction displays its output line:
j = 1
do forever
/* do some work here */
j = j + 1
if j = 4 then
signal my_routine /* unconditionally go to the label MY_ROUTINE */
end
my_routine:
say ‘SIGNAL instruction was executed, MY_ROUTINE entered...’
signal differs from the goto of some other languages in that it terminates all active control structures in
which it is encoded. You could not transfer control to another point in a loop using it, for example.
49
Chapter 3
Duplicate labels are allowed within Rexx scripts, but control will always be transferred to the one that
occurs first. We recommend that all labels in a program be unique within a program for the sake of
readability.
In an entirely different role, the signal instruction is also used to capture or “trap” errors and
special conditions. Chapter 10 discusses this in detail. This is a special mechanism within the Rexx
language designed to capture unusual error conditions or “exceptions.”
The last unstructured instruction to discuss is the iterate instruction. The iterate instruction causes
control to be passed from the current statement in the do loop to the bottom of the loop. In this example,
the iterate instruction ensures that the say instruction never executes. The if instruction’s
condition evaluates to TRUE every time that statement is encountered, so the iterate instruction
reruns the do loop and the say instruction never runs:
j = 1
do until j = 4
/* do some work here */
j = j + 1
if j > 1 then iterate
say 'This line is never displayed!' /* this line will never execute */
end
Summary
This chapter summarizes Rexx’s structured and unstructured control constructs. These include the if,
do, select, call, exit, and return instructions for structured programming, and the unstructured
iterate, leave, and signal instructions. The do until and do forever forms of the do
instruction are also unstructured.
For more background on why structured programming is beneficial -- especially for larger and more
complicated programs -- refer to the works of Edward Yourdon and Edsger Dijkstra.
Use the instructions this chapter covers to direct conditional logic as in most other programming
languages. This chapter presented many small code snippets to illustrate how to use the instructions that
control program logic. Subsequent chapters will provide many more examples of the use of these
instructions. These upcoming examples demonstrate the instructions in the more realistic context of
complete programs. They will make the use of the instructions for the control of logical flow much
clearer.
50
Control Structures
51
4
-J
Arrays
Overview
Every programming language provides for arrays. Sometimes they are referred to as tables. This
basic data structure allows you to build lists of “like elements,” which can be stored, searched,
sorted, and manipulated by other basic programming operations.
You’ll sometimes hear arrays referred to as compound variables or stem variables in Rexx. We’ll
explain why in just a moment.
Rexx’s implementation of arrays is powerful but easy to use. Arrays can be of any dimensionality.
They can be a one-dimensional list, where all elements in the array are of the same kind. They can
be of two dimensions, where there exist pairs of entries. In this case, elements are manipulated by
two subscripts (such as I and J). Or, arrays can be of as many dimensions as you like. While Rexx
implementations vary, the usual constraint on the size and dimensionality of array is memory. This
contrasts with other programming languages that have specific, language-related limitations on
array size.
Rexx arrays may be sparse. That is, not every array position must have a value or even be initial
ized. There can be empty array positions, or slots, between those that do contain data elements. Or
arrays can be dense, in which consecutive array slots all contain data elements. Figure 4-1 below
pictorially shows the difference between sparse and dense arrays. Dense arrays are also sometimes
called nonsparse arrays.
Arrays may be initialized by a single assignment statement. But just like other variables, arrays are
defined by their first use. You do not have to predefine or preallocate them. Nor must you declare
a maximum size for an array. The only limitation on array size in most Rexx implementations is
imposed by the amount of machine memory.
Chapter 4
A Dense Array
Figure 4-1
You can refer to individual elements within a Rexx arrray by numeric subscripts, as you do in other pro
gramming languages. Or, you can refer to array elements by variables that contain character strings.
Rexx then uses those character strings as indexes into the array. For this reason, Rexx arrays are some
times termed content addressable. They can be used as a form of associative memory, in that they create an
association between two values. This permits innovative use of arrays in problem solving. We’ll explain
what these terms mean and why are they important in more detail later in the chapter. We’ll even give
several examples of how content addressable memory structures are useful in resolving programming
problems. For now, remember that the subscripts you encode to access individual array elements can be
either numeric or string variables.
Like many scripting languages, Rexx lacks complex data structures such as lists, trees, records, and the
like. These are unnecessary because by understanding content-addressable arrays it is easy to build
these structures. Rexx arrays provide the foundation to build any imaginable data structure. We’ll show
you how later in this chapter. First, let’s explore the basics of how to code arrays and process their data
elements.
The Basics
To approach the subject of arrays, let’s review the way variable names are created. The basis for Rexx
arrays are compound variable names or symbols. So far we’ve seen several kinds of symbols within Rexx:
54
Arrays
Simple symbolsare synonymous with variable names, as we have known them thus far, while compound
symbols contain one or more periods. Compound symbols are the basis for arrays.
In compound symbols, the stemis the part of the name up to and including the first period. The stem is
sometimes called a stem variable. The tailcomprises one or more symbols separated by periods.
□ list.j — list. is the stem of the array. Note that the stem name includes the period.
□ books.j.k — books. is the stem, j.k is the tail. j and k are two subscripts.
In these examples, Rexx substitutes in the value of the variables j and k before referencing into the
arrays. These values can be numbers, but they do not have to be. Rexx allows indexing into an array
based on any variable value you encode, whether it is numeric or a string value.
Here is a sample series of statements that refer to an array element based on a string value in a variable.
The first line below initializes all elements in an array to the null string (represented by two back-to-back
quotation marks). The second line assigns a value to a specific array element. The last two statements
show how a character string provides a subscript into the array to retrieve that data element from the
array:
It is probably worth noting that Rexx uppercases the string cherry into cherry in the subscript assign
ment statement above because that character string is not enclosed in quotation marks. Rexx also upper
cases variable names such as fruit.cherry into fruit.cherry internally. Had we coded
subscript_string = ‘cherry’ as the third line in the sample code, it would not work properly. The
array tail is uppercased internally by Rexx so the subscript string used for finding the data element must
also be uppercase.
What happens if you accidentally reference an array with a subscript that is not yet initialized? Recall
that in Rexx an uninitialized variable is always its own name in uppercase. So, if my_index has not yet
been assigned a value, my_array.my_index resolves to my_array.my_index. Oops! This is probably
not what the programmer intended.
Initialize the array as a whole by referring to its stem. The dimensionality of the array does not matter to
this operation. We saw one example of initializing an entire array in one line of the sample code. Here
are some more examples:
55
Chapter 4
You cannotperform other kinds of operations on entire arrays by single statements—in most Rexx
implementations. For example, these statements are invalidand result in errors:
To process all the elements in an array, use a do loop. This works as long as the array is indexed or sub
scripted by numeric values, and each position, or slot, in the array contains a value. To process all the
elements in the array, you must keep track of the maximum subscript you use. There is no Rexx function
that returns the largest numeric subscript you’ve used for an array. Here is an example that shows how
to process all the elements of an array. In this code, each contiguous array position contains an element,
and the array subscript is numeric:
Another technique for array processing is to initialize the array to zeroes for numeric values, or to the
empty string or null stringfor character string entries (represented by two back-to-back quotation
marks ‘’ ). Then process the array starting at the beginning until you encounter an entry set to the ini
tialization value. Here’s sample code that processes all elements of an array based on this approach:
If you take this approach, be sure that the value used for initialization never occurs in the data you place
into the array!
This approach also assumes a nonsparse, ordense,array— one in which the positions in the array have
been filled consecutively without skipping array slots or positions. For a sparse array, we recommend
storing the largest numeric subscript you use in a variable for future reference. Obviously, you cannot
simply process a sparse array until you encounter the array initialization value because some positions
within the array may not contain data items. In processing a sparse array, your code will have to be able
56
Arrays
to distinguish between array positions that contain valid values and those that do not. For this reason, it
is useful to initialize all sparse array elements to some unused default value (such as the null string or
zeroes) prior to using the array.
In many programming languages, you must be concerned with what the subscript of the first entry in a
table is. Is the first numeric subscript 0 or 1? In Rexx, the first subscript is whatever you use! So, input
the first array element into position 0 or 1 as you prefer:
or
Just be sure that whatever choice you make you remember and that you remain consistent in your
approach. This flexibility is a handy feature of content-addressable arrays.
As an informal convention, many Rexx programmers store the number of array elements in position 0,
then start storing data elements in position 1:
Assuming that the array is not sparse and the index is numeric, process the entire array with code like this:
do j = 1 to array_name.0
say “Here’s an array element:” array_name.j
end
Placing the number of array elements into position 0 in the array is not required and is strictly an infor
mal convention to which many Rexx programmers adhere. But it’s quite a useful one, and we recom
mend it.
A Sample Program
This sample program illustrates basic array manipulation. The program defines two arrays. One holds
book titles along with three descriptors that describe each book. The other array contains keywords that
will be matched against the three descriptors for each book.
The user starts the program and inputs a “weight” as a command line parameter. Then the program lists
all books that have a count of descriptors that match a number of keywords at least equal to the weight.
This algorithm is called weighted retrieval, and it’s often used in library searches and by online biblio
graphic search services.
57
Chapter 4
Here’s the entire program. The main concepts to understand in reviewing it are how the two arrays are
set up and initialized at the top of the program, and how they are processed in the body. The do loops
that process array elements are similar to the ones seen previously.
/* FIND BOOKS: */
/* */
/* This program illustrates basic arrays by retrieving book */
/* titles based on keyword weightings. */
say ‘For weight of’ weight ‘retrieved titles are:’ /* output header */
58
Arrays
The program shows that you can place more than one Rexx statement on a line by separating the state
ments with a semicolon. We use this fact to initialize the searchable keywords. Here’s an example with
two statements on one line:
To implement the weighted-retrieval algorithm, the outermost do loop in the script processes each book,
one at a time. This loop uses the variable j as its subscript:
The do loop could have included the phrase by 1, but this is not necessary. Rexx automatically defaults
to incrementing the loop counter by 1 for each iteration. If we were to encode this same line and explic
itly specify the increment, it would appear like this. Either approach works just fine:
The loop that processes each book, one at a time, is the outermost loop in the code. The next, inner loop
uses k as its control variable and processes all the keywords for one book:
The innermost loop uses l for loop control and inspects the three descriptors for each book title. This
code totals how many of each book’s descriptors match keywords:
If the count or weightthis loop totals is at least equal to that input as the command line argument, the
book matches the retrieval criteria and its title is displayed on the user’s screen.
This script is written such that the programmer does not need to keep track of how many variables any
of the arrays contain. The while keyword processes items in each do loop until a null entry (the null
string) is encountered. This technique works fine as long as these two conditions pertain:
Its approach to array prrceccing makes the program code independent of the number of broke and key-
wrrfl it mslt crrcell. Thil ftexibititi wrstf attrw the lame attrrithm tr crrcell incst frrm fitel, frr
exampte. So, it woutd be eacy to etiminate the ctatic accignment ctatementc in thic program and reptace
them with cariabte input read in from one or more input fitec. You can cee that the approach thic ccript
takec to array proceccing procidec great ftexibitity.
59
Chapter 4
The script demonstrates that nested array processing and simple logic can provide sophisticated
weighted retrievalby applying search terms to item descriptors. From the standpoint of Rexx arrays, it
shows how to nest array-processing do loops and terminate those loops when all items in the arrays
have been processed.
Associative Arrays
The sample program indexed its tables by numeric subscripts. The script processed the arrays simply by
incrementing the numeric subscripts during do loops. But Rexx also allows subscripts to be variables
that contain character strings. Let’s discuss this approach now.
Associative arrays subscript entries by character strings. You can use them to create key-value pairs. Here’s
an example. We’ve created an array of months called the month array. In initializing this array, we’ve
placed multiple assignment statements per line. We accomplish this by separating individual statements
by semicolons:
This array associates months with their ordinal positions in the calendar. For example, if you want to
know what the 12th month is, referencing month.12 returns december. We’ve established a group of
keys that return specific values.
Combined with the previous array, the following code returns the calendar position of any given month:
say ‘Enter the calendar position of the month you want displayed...’
pull monthly_position .
say ‘Month number’ monthly_position ‘is’ month.monthly_position
Notice that the month is returned in uppercase letters. This is because the month names were not
enclosed in quotation marks when the array values were initialized. So Rexx uppercased them. To retain
lowercase alphabetics for the month names, simply enclose the initialization strings in quotation marks
(as was done in the sample program that performed the weighted-retrieval algorithm). Here’s how to
initialize data elements to retain the lowercase month names:
60
Arrays
The month array in this problem represents a set ofkey-value pairs. A key-value pair is a simple data
structure that can be used to resolve a wide range of programming problems. Let’s take a look at a com
plete sample script that illustrates their use.
D:\Regina\pgms>regina code_lookup.rex
For which town do you want the area code?
Chicago
The area code for CHICAGO is 312
For which town do you want the area code?
Homewood
The area code for HOMEWOOD is 708
For which town do you want the area code?
Cincinnati
Town CINCINNATI is not in my database
For which town do you want the area code?
Zion
The area code for ZION is 847
For which town do you want the area code?
<user presses <ENTER> key and leaves the program>
/* CODE LOOKUP: */
/* */
/* Looks up the areacode for the town the user enters. */
61
Chapter 4
The program first initializes the entire area array to the null string by the single assignment statement.
It sets all entries in that array to the null string (represented by two back-to-back single quotation
marks ‘’ ):
Next, six assignment statements set the area codes for specific towns. This will be the lookup tablefor the
area codes. This lookup table could be considered a list of key-value pairs:
If the array element area .town is equal to the null string, then this array slot was not assigned a value -
the program tells the user that the town is not in the area code database. Otherwise, area.town repre
sents an area code value that the script reports to the user:
if area.town = ‘’
then say ‘Town’ town ‘is not in my database’
else say ‘The area code for’ town ‘is’ area.town
The program reports the desired area codes until the user enters the null string to the prompt to termi
nate interaction. The user enters the null string simply by pressing the <enter> key without entering
anything.
As in the previous programming example, be sure that you understand the use of case in this sample
script. The town is returned in uppercase because the tail of each array element is uppercased by Rexx.
Rexx views variable names internally as uppercased. The comparison with the town name the user types
in works properly because the pull instruction automatically translates the city name he or she enters
into all uppercase letters.
To summarize, this script shows how arrays can be subscripted by anyvalue (not just numeric values).
This supports content-addressableor associative arrays, a data structure concept that applies to a wide
range of programming problems. Here we’ve used it to implement a simple lookup table based on key
value pairs. Associative memory can also be applied to a wide range of more complex programming
problems. The next section discusses some of these applications.
62
Arrays
Arrays can have any number of dimensions to create more detailed associations. This forms the basis for
creating complex data structures. Subscript strings essentially become symbolic pointers, access points
that allow you to create content-addressable data structures. Scanning a table for a value becomes
unnecessary because content-addressability provides direct access to the desired entry. Using these prin
ciples you can create data structures such as lists, trees, records, C-language structs, and symbolic
pointer-based data structures.
In the sample program that retrieved book titles, the array named keywords is one-dimensional (it uses
just a single subscript). The data structure it represents is a list. The script implements its algorithm
through list processing.
In that script, the array named title has elements that are referred to either by one subscript (the book
title) or by two (the descriptors associated with each title). There is a hierarchical relationship — each
book has a set of descriptors. The data structure represented here is a tree. The logic that searches the
three descriptors for a specific book performs leaf-node processing.
Each root nodehas the same number of leaves(descriptors), so we have a balanced tree. But Rexx does not
require developers to declare in advance the number of elements an array will hold, nor that the tree be
balanced. We could have any number of descriptors per book title, and we could have any number of
leaves per tree. The algorithm in the program easily processes a variable number of array items and han
dles data structures composed of unknown numbers of elements. The Find Books program manages a
balanced tree, or B-tree, but could as well handled an unbalancedor skewed tree.
In the sample program that retrieved area codes, towns and their area codes were associated by means
of key-value pairs. This kind of data structure is widely used, for example, in lookup tables, “direct
access” databases, and Perl programming. It forms the conceptual basis of the popular embedded open
source database Berkeley DB. Even such a simple association can underlie high-powered application
solutions.
Figure 4-2 pictorially illustrates some of the basic data structures that can easily be created by using
arrays. That Rexx supports such a wide range of data structures, without itself requiring complex syn
tax, shows how a “simple” language can implement sophisticated processing. This is the beauty of Rexx:
power based on simplicity.
63
Chapter 4
Key 1 Value 1
Key 2 Value 2
Key 3 Value 3
b.1
Key 4 Value 4 b.1.1
Key-value Pairs b.1.1.1
or Look-up Table b.1.1.2
b.2
b.2.1
b.1 b.1 b.2.2
b.1.1 b.1.1 b.2.3
b.1.2 b.2 b.3
b.2 b.2.1 b.3.1
b.2.1 b.2.2 b.3.1.1
b.2.2 b.2.3 b.3.1.1.1
b.3 b.3
b.3.1 A Multi-level Tree
b.4
b.3.2 (unbalanced)
Summary
Rexx supports content-addressable arrays that can be of any dimensionality. These arrays can be initial
ized as an entity by referring to the array stem. However, other kinds of whole-array manipulation
based on the stem are not permitted in standard Rexx. Array sizes do not have to be declared or defined
prior to use, and sizes are limited only by the size of memory in most Rexx implementations.
Arrays provide a way to build the more powerful data structures that compiled languages sometimes
offer and scripting languages like Rexx “lack.” Symbolic pointers form the basis of content-addressable
data structures. Using-content-addressable arrays, you can easily build lists, trees, records, structures,
and other variable-length and variably sized data structures. Rexx simplifies the programmer’s task
because no complicated language elements are necessary to implement advanced data structures. The
syntax remains clean and simple, even while the data structures one builds become powerful and flexible.
64
Arrays
65
c
Rexx provides a simple-to-use, high-level I/O interface. At the same time, Rexx aims for standard
ization and portability across platforms. Unfortunately, this latter goal is difficult to achieve — I/O
is inherently platform-dependent, because it relies upon the file systems and drivers the operating
system provides for data management. These vary by operating system.
This chapter describes the Rexx I/O model at a conceptual level. Then it explores examples and
how to code I/O. The last part of the chapter discusses some of the problems that any program
ming language confronts when trying to standardize I/O across platforms, some of the trade-offs
involved, and how this tension has been resolved in Rexx and its many implementations.
Rexx provides an I/O model that is easy to use and as portable as possible. Section II explores the
I/O extensions that many versions of Rexx offer for more sophisticated (but less portable) I/O.
Chapter 15 illustrates database I/O and how to interface scripts to popular database management
systems such as SQL Server, Oracle, DB2, and MySQL.
A stream may be either transient or prssistnnt. A transient stream could be the characters auser
enters through the keyboard. They are read; then they are gone. A persistent stream has a degree
Chapter 5
of permanency. Characters in a file, for example, are stored on disk until someone deletes the file con
taining them. Files are persistent.
For persistent streams only, Rexx maintains two separate, independent positions:a read positionand a
write position. The type of access to the persistent stream or file determines which of these positions make
logical sense. For example, for a file that a script reads, the read position is important. For a file that it
writes, the write position is important.
The read and write positions for any one file may be manipulated by a script independently of one
another. They might be set or altered explicitly. Normally, they are altered implicitlyas the natural result
of read or write operations.
Programs can process streams in either of two modes: character by character or line by line. Rexx pro
vides a set of functions to perform I/O in either manner. These are typically referred to as character-
oriented I/O and line-oriented I/O. Figure 5-1 summarizes these two basic I/O modes.
A stream is typically processed in either one of the two I/O modes or the other. However, it is possible to
intermix character- and line- oriented processing on a single stream.
Like many programming languages, Rexx recognizes the concept of standard inputand standard output.
The former is the default location from which input is read, and the latter is the default location to which
output is written. These defaults are applied when no specific name is encoded in a Rexx statement as
the target for an I/O operation. Standard input is normally the keyboard, and standard output is the dis
play screen. Standard Rexx does not include the concept of a standard error stream.
As with variables, Rexx files are defined by their first use. They are not normally predefined or
“declared.” In standard Rexx, one does not explicitly “open” files for use as in most programming lan
guages. Files do not normally need to be closed; they are closed automatically when a script ends. For
most situations, this high level of automation makes Rexx I/O easy to use and convenient. For complex
68
Input and Output
programs with many files, a situation in which memory is limited, or when a file needs to be closed and
reopened, Rexx provides a way to explicitly close files.
Thls sample scrlpt reads all llnes ln an lnput flle, and wrltes those contalnlng the phrase payment
overdue to an output flle. (A form of thls slmple scrlpt actually found a number of lost lnvolces
and saved a small constructlon company tens of thousands of dollars!):
/* FIND PAYMENTS: */
/* */
/* Reads accounts lines one by one, writes overdue payments */
/* (containing the phrase PAYMENT OVERDUE) to an output file. */
To run thls program, enter the names of lts two arguments (the lnput and output flles) on the command
llne:
In thls code, the parse arg lnstructlon ls to arg as parse pull ls to pull. In other words, lt performs
the exact same functlon as lts counterpart but does not translate lnput to uppercase. arg and parse arg
both read lnput arguments, but arg automatlcally translates the lnput strlng to uppercase, whereas
parse arg does not. Thls statement reads the two lnput arguments wlthout automatlcally translatlng
them to uppercase:
69
Chapter 5
This statement:
shows how Rexx programmers often perform a read loop. The lines function returns a positive number
if there are lines to read in the input file referred to. It returns 0 if there are none, so this is an easy way to
test for the end of file. The do loop, then, executes repeatedly until the end of the input file is encountered.
The next program statement reads the next input line into the variable input_line. It reads one lineor
record, however the operating system defines a line:
The if statement uses the string function pos, which returns the position of the given string if it exists
in the string input_line. Otherwise, it returns 0. So, if the character string payment overdue occurs in
the line read in, the next line invokes the lineout function to write a line to the output file:
or
feedback = lineout(fileout,input_line)
The recommended approach uses the call instruction to run the lineout function, which automati
cally sets its return string in the special variable result. If the variable result is set to 0, the line was
successfully written, and if it is set to 1, a failure occurred. The sample script opts for clarity of illustra
tion over robustness and does not check result to verify the success of the write.
The second approach codes lineout as a function call, which returns a result, which is then assigned to
a variable. Here we’ve assigned the function return code to the variable feedback. You’ll sometimes see
programmers use the variable rc to capture the return code, because rc is the Rexx special variablethat
refers to return codes:
rc = lineout(fileout,input_line)
Now, here’s something to be aware of. This coding will not work, because the return string from the
lineout function has nowhere to go:
What happens here? Recall that the return code from a function is placed right into the code as a replace
ment for the coding of the function. So after this function executes, it will be converted to this if successful:
70
Input and Output
A standard rule in Rexx is that whenever the interpreter encounters something that is not Rexx code
(such as instructions, expressions to resolve, or functions), Rexx passes that code to the operating system
for execution. So, Rexx passes 0 to the operating system as if it were an operating system command! This
causes an error, since 0 is not a valid operating system command.
We’ll discuss this in more detail in Chapter 14, when we discuss how to issue operating system com
mands from within Rexx scripts. For now, all you have to remember is that you should either call a
function or make sure that your code properly handles the function’s returned result.
The lines function works slightly differently in different Rexx implementations. It always returns 0 if
there are no more lines to read. But in some Rexx interpreters it returns 1 if there are more lines to read,
while in others it returns the actual number of lines left to read. The latter produces a more useful result
but could cause Rexx to perform heavy I/O to determine this value.
The ANSI standard clarified this situation in 1996. Today ANSI-standard Rexx has two options:
For backward compatlblllty, the second casels the default. A true ANSI-standard Rexx wlll return 1 lf
you encode the lines function without specifying the optional parameter, and there are one or more
lines left to read in the file. However, some Rexx implementations will return the actual number oi lines
left to read instead of following theANSI specification.
Standard Rexx does not permit explicitly opening files, but how about closing them? Rexx closes files
automatically when a script ends. For most programs, this is sufficient. The exception is the case where a
program opens many files and uses an exceptional amount of memory or system resources that it needs
to free when it is done processing files. Another example is the situation in which a program needs to
close and then reopen a file. This could happen, for example, if a program needed to sequentially pro
cess the same file twice.
How a file is closed or how its buffers are flushed is implementation-dependent. Most Rexx interpreters
close a file by encoding a lineout function without any parameters beyond the filename. Just perform a
write operation that writes no data:
call lineout ‘c:\output_file’ /* flushes the buffers and closes the file -
in most Rexx implementations */
The stream function is another way to close files in many implementations. stream allows you to either:
or
71
Chapter 5
The status check is ANSI standard, but the specific commands one can issue to control a file are left to the
choice of the various Rexx implementations. Here’s how to issue an ANSI-standard status check on a file:
or
The status values returned are those shown in the following table:
The commands you can issue through the stream function are completely dependent on which Rexx
interpreter you use. Regina Rexx allows you to open the file for reading, writing, appending, creating, or
updating; to close or flush the file, and to get its status or other file information. Regina’s stream func
tion also allows scripts to manually move the file pointers, as would be useful in directly accessing parts
of a file.
The file pointers may be moved in several ways. All Rexx scripts that perform input and/or output do
this implicitly, as the result of normal read and write operations. Scripts can also move the file pointers
explicitly. . . but these operations are implementation-specific. Some Rexx interpreters, such as Regina,
enable this via stream function commands, while others provide C-language-style seek and tell func
tions that go beyond the Rexx standard. Read your Rexx’s documentation to see what your interpreter
supports. Part II goes into how specific Rexx interpreters provide this feature and offers sample scripts.
The lineout, charout, linein, and charin functions provide the most standardized way to explicitly
control file positions, but care is advised. Most scripts just perform standard read and write operations
and let Rexx itself manage the file read and write positions. Later in this chapter we discuss alternatives
for those cases where you require advanced file I/O.
72
Input and Output
□ charout —Writes zero or more characters to an output stream. By default this writes to stan
dard output (usually the display screen). Returns 0 if all characters were successfully written.
Or, it returns the number of characters remaining after a failed write.
□ output (usually the display screen)—Returns 0 if all characters were successfully written. Or, it
returns the number of characters remaining after a failed write.
□ chars —Returns either 1 or the number of characters left to read in an input stream (which
could be 0).
This sample program demonstrates character-oriented input and output. It reads characters or bytes, one
by one, from a file. It writes them out in hexadecimal form by using the charout function. The script is a
general-purpose “character to hexadecimal” translator. Here is its code:
/* TRANSLATE CHARS: */
/* */
/* Reads characters one by one, shows what they are in hex format */
The script illustrates the use of the chars function to determine when the input file contains no more
data to process:
This character-oriented chars function is used in a manner similar to the line-oriented lines function
to identify the end-of-filecondition. Figure 5-2 below summarizes common ways to test for the end of a
file.
73
Chapter 5
The script uses the conversion function c2x to convert each input character into its hexadecimal
equivalent. This displays the byte code for these characters:
This script illustrates the charout function twice. The first time it includes a comma to replace the out
put filename, so the character is written to the default output device (the display screen). The second
charout function includes an output filename and writes characters out to that file:
Let’s take a look at some sample output from this script. Assume that the input file to this script consists
of two lines containing this information:
linel
line2
The hexadecimal equivalent of each character in the character string line1 is as follows:
linel
6C 69 6E 65 3l
With this information, we can interpret the script’s output. This output appears as shown, when run
under Linux, Unix, macOS, Windows, and DOS. Linux, Unix, and macOS terminate each line with a
line feed character (x'0A'). This character is also referred to as the newline character or sometimes as
the linefeed. Windows and DOS end each line with the pair of characters for carriage return and line
feed (x'0D0A'):
Linux: 6C 69 6E 65 31 0A 6C 69 6E 65 32 0A
Unix: 6C 69 6E 65 31 0A 6C 69 6E 65 32 0A
Windows: 6C 69 6E 65 31 0D 0A 6C 69 6E 65 32 0D 0A
DOS: 6C 69 6E 65 31 0D 0A 6C 69 6E 65 32 0D 0A1A
macOS: 6C 69 6E 65 31 0A 6C 69 6E 65 32 0A
You'll often see "linefeed" abbreviated as LF, and "carriage return" as CR. So Linux, Unix, and macOS
terminate lines with LF, while Windows uses CR LF.
A few operating systems mark the end of the file by a special end-of-file character. This byte occurs once at
the very end of the file. DOS is an example. It writes its end-of-file character Control-Z or x'1A' at the
very end of the file.
This example shows two things. First, what Rexx calls character I/O is really “byte-oriented” I/O. Bytes
are read one by one, regardless of their meaning to underlying operating system and how it may use
special characters in its concept of a file system. Rexx character I/O reads every byte in the file, including
the end-of-line or other special characters.
74
Input and Output
Second, character I/O yields platform-dependent results. This is because different operating systems
manage their files in different ways. Some embed special charactersto denote line end, others don’t, and
the characters they use vary. Character I/O reads these special characters without interpreting their
meanings. Line-oriented I/O strips them out. If you want only to read lines of data or I/O records in
your script, use line-oriented I/O. If you need to read allthe bytes in the file, use character I/O.
Character I/O is easy to understand and to use. But it is often platform-dependent. If you’re concerned
about code portability, be sure to reference the operating system manuals and code to handle all situa
tions. Or, stick to line-oriented I/O, which is inherently more portable.
Conversational I/O
A user interaction with a script is termed a conversationor dialogue. The interactive process is called con
versational I/O. When writing a Rexx script that interacts with a user, one normally assumes that the user
sees program output on a display screen and enters input through the keyboard. These are the default
input and output streams for Rexx.
To output information to the user, code the say instruction. As we’ve seen, the operand on say can be
any expression (such as a list of literals and variables to concatenate). say is equivalent to this call to
lineout, except that say does not set the special variable result:
The comma indicates that the instruction targets standard output, normally the user’s display screen.
Use pull to read a string from the user and automatically translate it to uppercase, or use parse pull
to read a string without the uppercase translation. Both instructions read user input into a template, or
list of variables. Discard any unwanted input beyond the variable list by encoding a period (sometimes
referred to as the placeholder variable).
This statement reads a single input string and assigns the first three words of that string to the three
variables. If the user enters anything more than three words, Rexx discards it because we’ve encoded the
period placeholder variable at the end of the line:
Redirected I/O
I/Oredirectionmeans you can write a program using conversational I/O, but then redirect the input
and/or output to other sources. Without changing your program, you could alter its input from the key
board to an input file. The pull or parse instructions in the program would not have to be changed to
make this work. Similarly, you could redirect a script’s say instructions to write output to a file instead
of the display screen, without changing your program code.
75
Chapter 5
Here is how to redirect I/O. Just run the script using the redirection symbols shown in this table:
How’s how to invoke the Four-Letter Words program of Chapter 3 with input from a file instead of the
keyboard:
The file four_letter_words.input consists of one word per line (so it conforms to the program’s
expectation that it will read one word in response to each prompt it gives). Here’s how to give the script
input from a file and redirect its output to a file named output.txt as well:
Redirected I/O is a very powerful concept and a useful testing tool. You can write programs and change
their input source or output destination without changing the script!
But redirection is operating-system-specific. Operating systems that support redirected I/O include
those in the Linux, Unix, BSD, Windows, and DOS families.
A warning about Windows— members in the Windows family of operating systems do not handle I/O
redirection consistently. Different versions of Windows handle I/O redirection in slightly different ways.
This has long been an issue for programmers who want their programs to run across many Windows
versions. This is not a Rexx issue, but rather an inconsistency in the behavior of Windows operating sys
tems. If you rely on redirection under Windows, you will have to test your scripts on each version of the
operating system they run on to ferret out any Windows inconsistencies.
I/O Issues
I/O is operating system dependent and thus presents a difficult issue for any programming language.
The reason is the inherent tension between an I/O model that is easy to use, easy to understand, and
portable—versus the desire to take advantage of operating-system-specific features for file system
manipulation.
Rexx always promotes ease of use and portability. Fitting with this philosophy, simplicity trumps OS-
specific features and maximizing I/O performance. So, the ANSI standard Rexx I/O model is simple
and portable. It does not take advantage of OS-specific I/O features or optimize I/O by platform.
76
Input and Output
Standard Rexx recognizes the trade-off between I/O portability and OS-specific I/O features by includ
ing functions such as stream and the options instruction, which are open ended and permit operands
beyond the ANSI standard. This allows Rexx interpreters to add I/O extensions within the context of the
ANSI standard that go beyond the standard to leverage OS-specific features.
The second section of the book describes the I/O extensions that different Rexx interpreters provide to
leverage OS- specific I/O features. For example, in the mainframe environment, it's not uncommon to
see all file I/O handled exclusively with the mainframe-unique EXECIO instruction. Appendix N
shows how to code and use EXECIO.
This chapter assumes the user interface to consist of a screen display and keyboard, and that disk I/O
means manipulating data residing in files. Of course, many programs require more advanced I/O and
different forms of user interfaces. Upcoming chapters cover these topics. Chapters 15 and 16, for exam
ple, describe and illustrate both database I/O and screen I/O using various GUI packages. Chapter 17
discusses Web interfaces for Rexx scripts. Section II illustrates the I/O extensions in many Rexx inter
preters that provide more sophisticatedfile processing.
Summary
This chapter provides an overview of the Rexx I/O model and how it is implemented in standard func
tions for line- and character-oriented I/O. We discussed conversational I/O and how to redirect I/O
under operating systems that support it. Redirection is a powerful debugging tool and provides great
flexibility, because the source of input and target for output for scripts can be altered without changing the
scripts themselves. The flexibility that redirection provides is very useful during script testing and
debugging.
Two I/O related topics will be covered in upcoming chapters. The external data queue or stack is an area of
memory that can be used to support I/O operations. The second important topic is I/O error
handling. Both are covered in future chapters.
Upcoming chapters also cover I/O through interface packages, such as databases, GUI screen handlers,
Web server interfaces, and similar tools.
77
String Manipulation
Overview
String manipulation meansparsing, spiicing, andpasting together character strings, sets of consecu
tive characters. Rexx excels at string manipulation. This it important for a wider variety of reasons
than may be apparent at first. Many programming problems are readily conceived of as operations
on strings. For example, building commands to issue to the operating system is a really a string
concatenation exercise. Analyzing the feedback from those commands once they are issued means
text analysis and pattern matching. Much of the data formatting and reporting that IT organiza
tions perform requires string processing. Data validation and cleansing require text analysis.
In a broad sense, many programming problems are essentially exercises in "symbol manipula
tion." String processing ss ameans to achievegeneric symbol manipulation.
The applications that these techniques underlie are endless. Everything from report writing, to
printing mailing labels, to editing documents, to creating scripts for systems administration, to
scripts that configure the environment, rely onstring manipulation.
This chapter introduces Rexx's outermost operators, functions, and pattern-matching capabilities.
We show you the features by which Rexx supports string processing so that you will combine
them in new ways to address the programming problems you face.
Bifurcation
Splits a string
Parsing
We’ve already seen that Rexx supports three ways of concatenating strings. These are:
The three styles of cnwcohewohinw cow be intermixed within statements. Cnwcohewohinw moy occur wher-
evei expieeeiine aon be ailel. Heie oie eice eocpie enonecenne itn in eeqtenae:
apple=’-Apple’
say ‘Candy’ || ‘ ‘ ||apple || ‘ ‘ || ‘Rodeo’
/* displays: ‘Candy -Apple Rodeo’ */
say ‘Candy’apple /* displays: ‘Candy-Apple’ */
say ‘Candy’ apple /* displays: ‘Candy -Apple’ */
say ‘Candy’appleapple ‘Rodeo’ /* displays: ‘Candy-Apple -Apple Rodeo */
80
String Manipulation
We’ve also seen several simple examples of string parsing. The arg instruction retrieves the arguments
sent in to a program or internal function and places them into a list of variables. Its general format is:
arg [template]
The templateis a list of symbols separated by blanks and/or patterns. The pull instruction operates in
the same manner as arg, reading and parsing a string input by the user into a list of variables. The input
string is parsed (separated) into the variables in the list, positionally from leftmost to rightmost, as sepa
rated by one or more spaces. The spaces delimiting the strings are stripped out, and the variables do not
contain any leading or trailing blanks.
There are two special cases to consider when a script reads and parses input by the arg or pull instruc
tions. The first is the situation in which more arguments are passed in to the routine than the routine
expects. Look at this case:
a contains: ONE
b contains: 2
c contains: THREE ‘4’
The last (rightmost) variable c in the variable list contains all remaining (unparsed) information. The
rule is: If you code too few input variables to hold all those that are input, the final variable in the input list con
tains the extra information. Remember that you could just ignore this extra information by coding a
period:
program: pull a b c .
a contains: ONE
b contains: 2
c contains: THREE
The ‘4’ is simply eliminated from the input by the placeholder variable, the period at the end of the pull
instruction input list or template.
The second situation to consider is if too few arguments are passed in to the receiving routine. Say that
the script issues a pull instruction to read input from the user. If too few elements are input by the user,
any variables in the list that cannot be assigned values are set to null:
a contains: ONE
b contains: 2
c contains: ‘’ /* c is set to the null string */
81
Chapter 6
Variable c is set to the null string (represented by back-to-back quotation marks, ‘’ ). This is different
from saying that the variable is uninitialized, which would mean its value is its own name in uppercase.
If the last variable were uninitialized, it would be set to ‘C’.
The templateis a list of symbols separated by blanks and/or patterns. upper means uppercase transla
tion occurs. Its presence is optional on the parse instruction. To avoid uppercase translation, just leave
the upper keyword out of the parse instruction.
Let’s look at the parse instruction in more detail. This form of the instruction parses an expression:
The expressionevaluates to some string that is parsed according to the template. The template provides
for three basic kinds of parsing:
Parsing by Template
By Numeric
Pattern abcabcabc
Columns: 1 5 9
Figure 6-2
You ace alceavy famrlrac nrhh pacirng ty nocvi. Thri ri nhece ne uie parse ho iepacahe a lrih of ele-
menhi rnho rnvrvrvual componenhi taiev on rnhecvenrng tlanli. Leh’i pacie an rnhecnahronal helephone
numtec ai an example.
82
String Manipulation
phone = ‘011-311-458-3758’
parse value phone with a b
This is a parse by wordsor blank separators. Since there are no blank separators anywhere within the
input string, the results of the parse instruction are:
a = 011-311-458-3758
b = ‘’ /* b is assigned the null string. */
Obviously, the dash ( - ) here is the separator, not the blank. Let’s try parsing by pattern, using the dash
( - ) as the separator or delimiter:
parse value phone with country_code ‘-’ area_code ‘-’ prefix ‘-’ suffix
country_code = 011
area_code = 311
prefix = 458
suffix = 3758
If there were more information in the input variable, regardless of whether or not it contained more dash
delimiters, it all would have been placed into the last variable in the list, suffix. If there are too few
strings in the input variable list, according to the parsing delimiter, then extra variables in the variable
list are assigned null string(s).
The patterncan be supplied in a variable. This yields greater programmability and flexibility. In this case,
enclose it in parentheses when specifying it in the template:
This parse instruction gives the same results as the previous one with the hardcoded delimiter dashes.
The advantage to placing the separator pattern in a variable is that we can now parse a different, inter
national designation for this phone number using the same parse instruction, just by changing the sepa
rator inside the pattern variable:
phone = ‘011.311.458.3758’
sep = ‘.’ /* The period is the Swiss delimiter for phone numbers ... */
parse value phone with country_code (sep) area_code (sep) prefix (sep) suffix
The same parse instruction properly separates the constituent pieces of the phone number with this
different delimiter. So, supplying the separator pattern in a variable gives scripts flexibility in parsing
operations.
Now parse by numbers. These represent column positionsin the input. Run:
phone = ‘011-311-458-3758’
parse value phone with country_code 5 area_code 9 prefix 13 suffix
83
Chapter 6
Here are the results from this statement:
country_code = 011-
area_code = 311-
prefix = 458-
suffix = 3758
Oops! You can see that parsing by numbers goes strictly by column positions. Delimiters don’t count.
Add these extra columns positions to eliminate the unwanted separators:
This gives the intended results because it parses out the unwanted separators by column positions:
country_code = 011
area_code = 311
prefix = 458
suffix = 3758
These are absolutecolumn positions. Each refers to an absolute column position, counting from the
beginning of the string.
Placing a plus ( + ) or minus ( - ) sign before any number makes its position relative to the previously
specified number in the list (or 1 for the first number). You can mix absolute and relative positions
togetter in tte same template, and even use negative numbers (wtict move tte relative position back
wards to tte left) but be careful. Unless you tave a situation ttat really requires it, jamming all tte pars
ing into one complex statement is rarely wortt it. Just code a series of two or ttree simpler statements
instead. Tten otters will be able to read and understand your code.
Ttis example properly parses tte ptone number witt bott absolute and relative column numbers. Tte
plus signs ( + ) indicate relative numbers. In ttis case, eact advances tte column position one ctaracter
beyond tte previous absolute column indicator:
country_code = 011
area_code = 311
prefix = 458
suffix = 3758
Witt ttis background, you can see ttat tte parse instruction provides real string-processing power.
Ttis example assigns tte entire teleptone number in tte variable phone to ttree new variables (kind of
like a ttree-part assignment statement):
84
String Manipulation
Now the variables phone_1, phone_2, and phone_3 all contain the same value as phone:
phone = ‘011.311.458.3758’
phone_1 = ‘011.311.458.3758’
phone_2 = ‘011.311.458.3758’
phone_3 = ‘011.311.458.3758’
In all the examples thus far, the input string was not changed. But it can be if encoded as part of the vari
able list. Here’s an example. Say that we have this variable:
This statement simply translates the employee’s name into uppercase and places it back into the same
variable:
This statement strips off the employee’s first name and places it into the variable first_name. Then it
puts the remainder of the name back into the employee_name variable:
The value keyword refers to any expression. You may also see the keyword var encoded when refer
ring specifically to a variable. In this case, you should not code the with keyword. This statement using
var gives the exact same results as the previous example with value and with:
A Sample Program
With this introduction to parsing, here’s a sample program to illustrate parsing techniques. This script
preprocesses the “load file” used to load data into a relational database such as DB2, Oracle, SQL Server,
or MySQL. The script performs some simple data verification on the input file prior to loading that data
into the database. This “data-cleansing” script ensures the data we load into the database is clean before
we run the database load utility. A script like this is useful because the data cleansing that database utili
ties typically perform is limited.
Here’s how the data will look after it’s loaded into the relational table:
85
Chapter 6
Databases like DB2, Oracle, and SQL Server accept input data in several different file formats. Two of the
most popular are comma-delimited filesand record-orientedor column-position files. Here’s an example of a
comma-delimited file:
10001,”George”,”Bakartt”,”307”
10002,”Bill”,”Wall”,”204”
10003,”Beverly”,”Crusher”,”305”
1x004,”joe”,”Zip”,”305”
10005,”Sue”,”stans”,”3x5”
Commas separate the four input fields. In this example, all character strings are enclosed in double quo
tation marks. Under operating systems that employ a file type, the file type for comma-delimited ASCII
files is typically *.del. This input file is named database_input.del .
Here is the other kind of file, a record file. Data fields start in specific columns. Fields are padded with
blanks, as necessary, so that the next field starts in its required column. Where file types are used this file
is typically of extension *.asc, so we’ve named this file database_input.asc :
10001George Bakartt307
10002Bill Wall 204
10003BeverlyCrusher305
1x004joe Zip 305
10005Sue stans 3x5
The program reads either of these two input file types. It determines which kind of file it is processing
by scanning the input text for commas. If the data contains commas, the program assumes it is dealing
with a comma-delimited ASCII file.
Then the program performs some simple data verification. It ensures that the emp_no and dept_no data
items are numeric, and that the first and last names both begin with capital letters. The script writes any
errors it finds to the display. Here’s the program:
86
String Manipulation
else do
parse value input_line with emp_no 6 fname 13 lname 20 dept_no
fname = strip(fname)
lname = strip(lname) /* remove trailing blanks */
end
end
So that we can easily feed it either kind of file to process, the script accepts the filename as an input
parameter. This technique of reading the name of the file to process from the command line is common.
It offers more flexibility than “hardcoding” the filename into the script.
To start off, the script reads the first line of input data and determines whether it is processing a
comma-delimited input file or a record-oriented file by this code:
The pos built-in function returns the character position of the comma (represented by the variable c)
within the target string. If the returned value is greater than 0, a comma is present in the input line, and
the program assumes that it is dealing with comma-delimited input. If the script finds no comma in the
input line, it assumes that it is dealing with a record-oriented input file.
If the program determines that it is working with a comma-delimited input file, it issues this parse
instruction to split the four fields from the input line into their respective variables:
parse value input_line with emp_no (c) fname (c) lname (c) dept_no
This parse statement strips data elements out of the input string according to comma delimiters. But there
is a problem. The second, third, and fourth data elements were enclosed in double quotation marks in the
input file. To remove these leading and trailing quotation marks, we use the built-in strip function:
fname = strip(fname,B,’”’)
lname = strip(lname,B,’”’) /* remove quote “ marks */
dept_no = strip(dept_no,,’”’)
The B operand stands for Both — strip out bothleading and trailing double quotation marks. Other
strip function options are L for leadingonly and T for trailingonly. Both is the default, so as the third
87
Chapter 6
line in the previous example shows, we don’t need to explicitly code it. Instead, we just show that
parameter is missing by coding two commas back-to-back. The final parameter in the strip function
encloses the character to remove within quotation marks. Here we enclosed the double quotation marks
( “ ) within two single quotation marks, so that strip will remove double quotation marks from the
variable’s contents.
If the script does not find a comma in the input line, it assumes that it is dealing with a file whose data
elements are located starting in specific columns. So, the script employs a parse by numberstatement,
where the numbers specify column starting positions:
If you program in languages like COBOL or Pascal, you might recognize this as what is often referred to
as record I/O. Languages like C, C++, and C# call this an I/O structure, or struct. Chapter 5 showed that
Rexx’s stream I/O model is simple, yet you can see that it is powerful enough to easily perform record
I/O by parsing the input in this manner. Part of the beauty of Rexx is that it is so easy to perform such
operations, without needing special syntax or hard-to-code features in the language to accomplish them.
After the parsing by number, the record input may contain trailing blanks for the two names, so these
statements remove them:
fname = strip(fname)
lname = strip(lname) /* remove trailing blanks */
Now that it has decoded the file and normalized the data elements, the program can get to work and
verify the data contents. This statement uses the datatype built-in function to verify that the emp_no
and dept_no fields (the first and last data elements in each input record) are numeric. If datatype does
not return the character string num, then one of these fields is not numeric and an error message is
displayed:
The logical or ( | ) is used to test both data elements in one if instruction. If either is not numeric,
the error message is displayed.
Finally, the script uses the verify built-in function to ensure that the two names both start with a capital
letter. First, this nested use of the substr built-in function returns the first letter of the name:
substr(fname,1,1)
Then the verify function tests this letter to ensure that it’s a member of the string consisting of all capi
tal letters:
The nestingof the substr function means that we have coded one function (substr) within another
(verify). Rexx resolves the innermost function first. The result of the innermost function is then
plunked right into the code at the position formerly occupied by that function. So, the substr function
88
String Manipulation
returns the first letter of the variable fname, which then becomes the first parameter within the paren
theses for the verify function.
Pretty nifty, eh? Rexx allows you to nest functions to an arbitrary depth. We do not recommend nesting
beyond a single level or else the code can become too complicated. We’ll provide an example of deeper
nesting (and how it becomes complicated!) later in this chapter.
It’s easy to code for intermediate resultsby breaking up the nesting into two (or more) statements. This
example shows how to eliminate the nested function to simplify the code. It produces the exact same
result as our nested example:
first_letter = substr(fname,1,1)
if verify(first_letter,’ABCDEFGHIJKLMNOPQRSTUVWXYZ ’) > 0 then
After the script runs, here is its output for the sample data we viewed earlier:
The last two lines of the input data contained several errors. Parsing techniques and string functions
together enabled the program to identify these errors.
String Functions
The parse instruction provides syntactically simple, but operationally sophisticated parsing. You can
resolve many string-processing problems with it. Rexx also includes over 30 string-manipulation func
tions, a few of which the sample script above illustrates.
This section describes more of the string functions. A later section in this chapter discusses the eight out
ermost functions that are word-oriented. The word-oriented functionsprocess strings on the basis of words,
where a wordis defined as a character string delimited by blanks or spaces. For example, this string con
sists of a list of 16 words:
now is the time for all good men to come to the aid of their country
Before we proceed, here is a quick summary of Rexx’s string functions (see Appendix C for full coding
details of these and all other Rexx functions):
89
Chapter 6
□ changestr — Changes all occurrences of one string within another to a specified string
□ compare — Tells if two strings are equal (like using the = operator)
□ overlay — Overlays one string onto another starting at a specified position in the target
□ strip — Strips leading and/or trailing blanks (or other characters) from a string
The changestr and countstr functions were added by the ANSI-1996 standard. Rexx implementations
that meet the TRL-2 standard of 1990 but not the ANSI-1996 standard may not have these two functions.
This is one of the few differences between the TRL-2 and ANSI-1996 standards (which are fully enumer
ated in Chapter 13). Regina Rexx fully meets the ANSI-1996 standard and includes these two functions.
Here’s a simple program that demonstrates the use of the abbrev, datatype, length, pos, translate,
and verify string functions. The script reads in four command-line arguments and applies data verifi
cation tests to them. The script displays any inaccurate parameters.
/* VERIFY ARGUMENTS: */
/* */
/* This program verifies 4 input arguments by several criteria. */
if abbrev(‘TESTSTRING’,first,4) = 0 then
90
String Manipulation
/* Second parm must consist only of digits and be under 5 bytes long */
if pos(third,first) = 0 then
say ‘Third parm must occur within the first:’ third first
if fourth = ‘’ then
say ‘You must enter a fourth parameter, none was entered’
uppercase = translate(fourth) /* translate 4th parm to uppercase */
if verify(uppercase,’ABC’) > 0 then
say ‘Fourth parm in uppercase contains letters other than ABC:’ fourth
The first parameter must be a valid abbreviation for a longer term. Where would you use this function?
An example would be a program that processes the commands that a user enters on a command line.
The system must determine that the abbreviation entered is both valid and that it uniquely specifies
which command is intended. The abbrev function allows you to specify how many characters the user
must enter that match the beginning of the target string. Here, the user must enter at least the four letters
test for a valid match:
if abbrev(‘TESTSTRING’,first,4) = 0 then
say ‘First parm must be a valid abbreviation for TESTSTRING:’ first
The second parameter the user enters must be numeric (it must be a valid Rexx number). The datatype
function returns the string num if this is the case, otherwise it returns the string char:
91
Chapter 6
datatype can also be used to check for many other conditions, for example, if a string is alphanumeric,
binary, lowercase, mixed case, uppercase, a whole number, a hexadecimal number, or a valid symbol.
Using the length function allows the program to determine if the second parameter contains more than
four characters:
The third parameter must be a substring of the first parameter. The pos function returns the starting posi
tion of a substring within a string. If the substring does not occur within the target string, it returns 0:
if pos(third,first) = 0 then
say ‘Third parm must occur within the first:’ third first
This code ensures that the user entered a fourth parameter. If a fourth parameter was not entered, the
argument will have been set to the null string (represented by the two immediately adjacent single quo
tation marks):
if fourth = ‘’ then
say ‘You must enter a fourth parameter, none was entered’
Finally, when translated to uppercase, the fourth parameter must not contain any letters other than A, B, or
C. Using the translate function with a single parameter translates the fourth argument to uppercase:
Use the verify function to ensure that all characters in a string are members of some set of characters.
This verify statement ensures that all the characters in the string named uppercase are members of its
second parameter, hardcoded here as the literal string abc. If this is not the case, the verify function
returns the position of the first character violating the rule:
The Rexx string functions are pretty straightforward. This script shows how easy it is to use them to per
form data verification and for basic string processing.
□ space—Formats words in a string such that they are separated by one or more occurrences of a
specified pad character
92
String Manipulation
□ wordpos — Returns the word position of the first word of a phrase (substring) within a string
These functions can be coupled with the outermost functions to address any number of programming
problems in which symbols are considered as strings of words. One such area is textual analysis ornatural
language processing. An example of a classic text analysis problem is to confirm the identity of the great
English playwright Shakespeare. Were all his works written by one person? Could they have been writ
ten by one his better-known contemporaries?
One way to answer these questions is to analyze Shakespeare’s works and look for word-usage patterns.
Humans tend to use words in consistent ways. (Some experts claim they can analyze word usage to the
degree that individuals’ linguistic profilesare unique as their fingerprints). Analyzing Shakespeare’s texts
and comparing them to those of contemporaries indicates whether Shakespeare’s works were actually
written by him or someone else.
Special-purpose languages such as SNOBOL are particularly adept at natural language processing. But
SNOBOL is premodern; it lacks good control constructs and robust I/O. Better to use a more main
stream, portable, general-purpose language like Rexx that offers strong string manipulation in the con
text of good structure.
Text analysis is a complex topic outside the scope of this book. But we can present a simple program that
suggests how Rexx can be applied to textual analysis. The script named Poetry Scanner reads modern
poetry and counts the number of articles and prepositions in the input. It produces a primitive form of
“sophistication rating” or lexical density. In our example, this rating comprises two ratios: the ratio of the
number of longer words to the number of shorter words, and the ratio of prepositional words to the total
number of words in the text.
To perform these operations, the script translates the input text to all uppercase and removes punctua
tion, because punctuation represents extraneous characters that are irrelevant to the analysis.
93
Chapter 6
. . . the program produces this output:
/* POETRY SCANNER: */
/* */
/* This program scans text to perform primitive text analysis. */
list_of_articles = ‘A AN THE’
list_of_preps = ‘AT BY FOR FROM IN OF TO WITH’
big_words = 0 ; small_words = 0
number_articles = 0 ; number_preps = 0
94
String Manipulation
The program demonstrates several of the word-oriented functions, including words, word, wordlength,
and wordpos. It also uses the translate function in two different contexts.
After it reads a line of input, the program shows how the translate function can be used with only the
input string as a parameter to translate the contents of the string to all uppercase letters:
Then translate is used again, this time to replace various punctuation characters with blanks. In this
call, the third parameter to translate contains the characters to translate, and the second parameter
tells what characters to translate them to. This example translates a various punctuation characters into
blanks:
The do loop processes the individual words in each input line. It executes while there is a word to pro
cess in the current input line:
The words function returns the number of blank-delimited words in the input line, line_str.
The wordlength function tells the length of the word. The script uses it to determine whether the word
is longer than 4 bytes:
The script needs to get an individual word in order to determine if that word is an article or preposition.
To parse out one word from the input string, the script invokes the word function:
To identify articles in the text, the program initializes a string containing the articles:
list_of_articles = ‘A AN THE’
Then it uses the wordpos function to see if the word being inspected occurs in this list of articles. word-
pos returns the starting position of the word in a string if it occurs in the string. If it returns 0, we know
that the word is not an article:
What this line of code really does is list processing. It determines if a given element occurs in a list. String
processing is easily used to emulate other kinds of processing techniques and various data structures,
such as the list. As mentioned in the chapter introduction, string manipulation is powerful because it is a
generic tool that can easily be used to implement other processing paradigms.
95
Chapter 6
The program ends with several say instructions that show how output can be dynamically concatenated
from the results of expressions. The last line of the program calculates a ratio and displays it with an
appropriate label:
Rexx evaluates the expression in parentheses prior to executing the say instruction and displaying the
output line. Remember that in evaluating expressions, Rexx always works from the innermost set of
parentheses on out. The script uses the parentheses to ensure that this expression is resolved first:
(big_words+small_words)
(number_preps/(big_words+small_words))
To summarize, this simple program illustrates a number of the word and string functions. More impor
tantly, it demonstrates that these features can be combined to create powerful string-processing scripts.
Rexx offers excellent string-processing facilities.
This encoding parallels that used to represent hexadecimal(or hex) strings. Hex is the base-16 arithmetic sys
tem by which computer bits are represented. Each character or byte is represented by two hex digits. Hex
strings are composed of the digits 0 thru 9 and letters A thru F, immediately followed by the letter x or X:
Binary strings find several uses. For example, use them to specify characters explicitly, bit by bit. This
helps you store and manipulate unprintable characters, for example. The relationship of bit strings to
characters is described by the table called a character map. Sometimes this is referred to as the character
encoding scheme.
Want to see your system’s entire character map? Just enter the xrange function:
Or display some portion of the character map by specifying a range of starting and ending points. The
range can be expressed in binary, hex, or character. You’ll see the entire map, just as shown earlier, if you
enter the entire range of the map explicitly:
96
String Manipulation
Display the same character map in hex (base-16) by using the c2x (character-to-hex) conversion function:
Want to see it as a bit string? You’ll have to do two conversions: character to hex, then hex to binary. Nest
the character-to-hex (c2x) function within the hex-to-binary (x2b) function to do this. Remember, Rexx
always evaluates the expression nested in the innermost parentheses first and works its way outward
from there. In this example, Rexx first performs the xrange function; then it executes c2x, and finally it
runs x2b, giving us the binary map in the end:
Bit strings have many applications. For example, database management systems manipulate bit map
indexesto provide quick access to data having a low variety of possible values (low cardinality) by
ANDing bit strings representing the data values. Another use for bit strings is in the technique called key
folding. This develops a key for direct (random) data access based on character string key fields. A logical
or bit operation is applied to the character field(s) to develop a key that is evenly distributed across
direct access slots or positions in the database or on disk. A similar technique called character foldingis
used to map similar characters to a common target, for example, to eliminate certain distinctions
between strings. This would be useful when you want similar strings to be compared as equal.
Rexx provides three binary string functionsthat perform logical operations on binary strings:
□ bitand—Returns the string result of two strings logically AND’d together, bit by bit
□ bitor — Returns the string result of two strings logically OR’d together, bit by bit
□ bitxor — Returns the string result of two strings logically EXCLUSIVE OR’d, bit by bit
Here are examples that apply these binary operations on bit strings. The binary string functions return
their results in the form of a character string (comprising one character, since 8 bits make a character and
the input strings we supply are one character long). Therefore, we use the character-to-hex (c2x) and
hex-to-binary (x2b) functions to interpret the result back to a displayable bit string:
The bitand operation sets bits to TRUE (1) in the result, only if they are TRUE in bothstrings. bitor sets
bits to TRUE (1) if they are TRUE in eitherstring. The bitxor function sets bits to TRUE only if they are
TRUE in exactly oneinput string or the other.
The next chapter covers data conversions in further detail and includes an sample program that demon
strates folding a two-part character key. It illustrates the bitand function and the c2x (character-to-
hexadecimal) and x2b (hexadecimal-to-binary) conversion functions.
97
Chapter 6
Summary
This chapter introduces string processing. It describes the basic techniques for concatenation and pars
ing in Rexx and lists the many built-in functions for string and word processing. The sample programs
demonstrate some of these techniques and functions.
The techniques we explored included concatenation, or the joining together of strings, and parsing, the
analysis and splitting of strings into their constituent substrings. We looked at a sample script that per
formed input data validation and saw how string analysis and parsing applied to this problem. Then we
looked at string functions, including those that analyze words, or discrete groups of letters surrounded
by spaces or blanks. Finally, we discussed bit strings. These can be used in a wide variety of applications,
such as database bit indexes and key folding. We discussed the major bit manipulation functions and
how bit strings are converted to and from other forms by using conversion functions.
Chapter 8 illustrates more string manipulation. It includes a script that can tell whether parentheses are
balanced (for example, as they might be coded within a Rexx statement). There is also a function called
Reverse, which reverses the characters in an input string, just like the Rexx built-in reverse function.
This new Reverse script does its work in an interesting way—it calls itself as its own subroutine. Stay
tuned!
98
Numbers, Calculations, and
Conversions
Overview
The second chapter gives the barest definition of what numbers are and how they are used. Rexx is
designed to handle arithmetic in as natural as manner as possible. It conforms to the basic rules of
computation that people absorb in high school or college. For most programs, you'll need no spe
cial knowledge of how Rexx handles numbers. Rely on its automatic numeric conversions and
rounding, and your scripts will work just fine.
Rexx differs from languages that place the burden of cross-system consistency on the developer.
Its language definition ensures that calculations provide the same outputs, regardless of language implemen
tation or the platform on which it is run.
Rexx achieves this cross-platform consistency by employing decimal arithmetic internally. This
contrasts with the floating-point or binary arithmetic used by most other programming languages,
which produce calculation results that can vary by platform. Rexx's natural or human-oriented
approach to computation is part of its appeal as an easy-to-use, portable scripting language.
Even with this high level of automation, there will be situations where you require some knowl
edge of how Rexx handles calculations and how you can affect them. This chapter probes a little
more deeply so that you'll be able to handle these situations appropriately. More specifically, we'll
look at the ways in which you can express numeric values within scripts. We'll discuss the
numeric functions for manipulating numbers, as well as the conversion functions that transpose
numbers to other forms. We'll also look at how to manage precision in calculations, and ways to
print or display numbers in the appropriately. The last part of the chapter focuses on the conver
sion functions that convert between numbers, character strings, bit strings, and hexadecimal val
ues. A sample script demonstrates several conversion functions in illustrating a programming
technique called key folding.
Chapter 7
The Basics
All Rexx variables are character strings. Numbers are just character strings whose contents are consid
ered numeric. Numbers are strings of one or more digits, optionally preceded by plus or minus sign ( +
or - ), and optionally containing a single period to represent a decimal point. Extending Rexx’s flexible
treatment of numbers, numbers may optionally have preceding or trailing blanks (which Rexx ignores
when calculating).
Numbers may also be expressed in two forms of exponential notation: scientificand engineering. Scientific
notation has one digit to the left of the decimal place, followed by fractional and exponential compo
nents. Engineering notation expresses the integer component by a number between 1 and 999. The E that
precedes the exponential portion of the number in either notation can be either uppercase or lowercase.
Spaces may not be embedded within the exponential portion of a number.
A string containing one of these forms of valid numbers will be recognized by Rexx as a number when
appropriate. For example, when two values are compared, Rexx implements a numeric comparisonif both
values are numeric. Otherwise, it employs a character comparison. The way Rexx performs the numeric
comparison internally is to subtract one number from the other. A result of 0 means that the two num
bers are the same; any other value indicates their difference.
□ Uesulre aredeterm inedtopho thu numOfr of sigmficdng digits (which defaults to 9).
□ Trag I zng zeroee arts intainedledcephwhenusing die power and diotsionoporators).
□ A reauit oi 0 ttrrpresedOed asnsg-dle-digit 0.
□ Reiuldi oee eddeeiiei it icietdiiic eddotetdiol tododiot ii eidhee dhe tunuee oi iifidi deioe do dhe
decimal point exceeds the setting for significoni digits or the number of digits following the drc-
inol doitd edceeii dtice dhe tunuer oi iiftiiicotd iifidi.
100
Numbers, Calculations, and Conversions
The term significant digitsrefers to how many digits are retained during a calculation. This is often
termed the precisionto which results are carried. Beyond this number of significant digits, or precision,
Rexx rounds off the number.
The default number of significant digits is 9. Remember the Poetry Scanner program in the previous
chapter? This is why it printed this output:
The nine digits to the right of the decimal point are the default number of significant digits (the default
precision). Use this simple command to alter the number of significant digits:
numeric digits 4
If you placed this statement prior to the calculations in the Poetry Scanner script, that same say instruc
tion would display:
This shows the power of the numeric digits instruction. With it you can alter or carry out accuracy to
any desired point.
numeric digits also determines whether your output appears in exponential notation. If you expect a
nonexponential result but Rexx gives you an exponential one, increasing the precision is one way to
change this.
The numeric instruction also has the fuzz keyword to indicate how many significant digits less than
that set by numeric digits will be involved during numeric comparisons. numeric fuzz onlyapplies
to comparisons. It has the effect of temporarily altering the number of significant digits for comparisons
only. Its value must be less than the setting of numeric digits. Its default is 0.
fuzz essentially controls the amount by which two numbers may differ before being considered equal.
For example, if numeric digits = 5 and numeric fuzz = 1, then numeric comparisons are carried
out to four significant digits.
Here’s a series of statements to demonstrate the effects of numeric digits and numeric fuzz. You
can see how their settings determine the precision of comparisons:
101
Chapter 7
numeric fuzz 1 /* set up to 1 from 0 to alter comparisons */
say 2.998 = 2.999 /* Displays: 1 */
say 2.998 < 2.999 /* Displays: 0 */
numeric form allows you to dictate which form of exponential notation is used. The default is scien
tific. To change this to engineering notation, enter:
Use the built-in functions digits, fuzz, and form to retrieve or display the current settings of numeric
digits, numeric fuzz, and numeric form, respectively. For example, assuming that you haven’t
changed the defaults, here’s what these functions return:
The only two errors Rexx gives from calculations are overflow/underflowand insufficient storage. The first
occurs when the exponential part of a number becomes too large or too small for the language inter
preter, while the second means Rexx ran out of memory.
Chapter 10 discusses and illustrates how to set up error or exception routines to handle or “trap” certain
kinds of error situations. One error you can manage by exception routines is the unintended loss of sig
nificant digits. This is achieved through the lostdigits condition, a feature added to Rexx by the
ANSI-1996 standard. Chapter 10 gives full details on the lostdigits condition and how to use it.
To control the display style of numbers, use the format built-in function:
format(number_string,before,after)
format rounds and formats a number. before indicates how many characters appear in the integer part
and after indicates how many characters appear in the decimal part.
If before is too small to contain the number, an error results. If after is too small, the number is
rounded to fit.
If before is larger than the integer requires, blanks precede the number. If after is larger than the deci
mal part requires, extra zeroes are added on the right.
With this information, another option in the Poetry Scanner script would have been to leave numeric
digits alone (letting it default it to 9 for all calculations), then format the output to reduce the number
of digits to the right of the decimal point:
outratio = number_preps/(big_words+small_words)
say ‘Ratio of preps/total words:’ format(outratio,1,4)
This yields the same result we got earlier from changing the value of numeric digits to 4:
102
Numbers, Calculations, and Conversions
say format(1234,2) /* error - not enough room for the integer part */
format can also be used to control the display of exponential numbers. This is the template for this ver
sion of format:
expp and expt control the formatting of the exponential part of the result. expp is the number of digits
used for the exponential part, while expt sets the trigger for the use of exponential notation. Here are a
few examples:
format(‘12345.67’,,,2,3) == ‘1.234567E+04’
format(‘12345.67’,,,4,4) == ‘1.234567E+0004
format(‘12345.67’,,2,,0) == ‘1.23E+4’
format(‘12345.67’,,3,,0) == ‘1.235E+4’
The format function is useful for generating reports with numbers nicely aligned in columns. Use it to
right-justify numbers and ensure that a consistent number of decimal places appear. Also use it to round
off numbers to any point of precision.
□ sign — Returns 1 if number is greater than 0, or 0 if the number is 0, or -1 if the number is less
than 0
□ trunc—Truncates a number
103
Chapter 7
Appendix C contains complete coding information for all these functions. Here, we will cover some of
their common uses.
It generates a random number between min and max (inclusive), based on the seed value. If you don’t
provide a seed, Rexx generates its own random number (usually based on the system time-of-day
clock). If min and/or max are not specified, they default to 0 and 999, respectively. Here are a couple
examples:
Many Rexx implementations offer extensions for transcendental mathematical functions. These include
tangent, sine, cosine, and the like. Section II covers these implementation-specific extensions to standard
Rexx when it discusses the features of the various open-source Rexx interpreters. Also, Appendix H lists
a few dozen of the many free and open-source Rexx tools and interfaces that are available. Among them
are several external function libraries that support advanced mathematics.
104
Numbers, Calculations, and Conversions
Conversions
Rexx variables contain values representing character, decimal, hexadecimal, and binary strings.
Obviously, there will be occasions when you need to convert variables from one of these representations
to another. Rexx provides a set of conversion functionsthat allow you to convert data between the differ
ent formats. Here is a list of these conversion functions:
Function Converts
The datatype function is useful in testing variables to see what kind of data they contain. datatype
without an option returns either the character string num or char to indicate whether the operand is
numeric or character:
As always, options to functions can be specified in either uppercase or lowercase. Here is the complete
set of options or tests for the datatype function:
105
Chapter 7
The string that datatype inspects can be of any representation: character, hex, or binary. The sample
program Verify Arguments in Chapter 6 showed how to use datatype in testing the values of user
input parameters.
A Sample Program
Here’s a sample program that uses the data conversion functions and the bitand bit string function.
This script takes two character fields and folds (logically ANDs) the bit representation of these character
fields together to create a direct access key. As mentioned in Chapter 6, this technique is called key folding
and can be used in developing a file manager or database system. It permits direct access to records
based on randomizing character keys. Here’s the program:
/* FOLDED KEY: */
/* */
/* This program folds a character key from two input fields. */
106
Numbers, Calculations, and Conversions
The program shows how to use built-in functions for conversions between data types. This statement
converts the original character string key to its hexadecimal equivalent through the c2x function:
Then, the x2b function converts that hex string to a binary string:
After both original character strings have been converted to binary, this statement logically ANDs the
two bit strings together to produce the folded key:
The original input fields the script folded contained the character strings key_field_1 and
key_field_2.
The last line in the output shows that ANDing these values together on the bit level only changes the
few bits at the end of the folded key string. These two key values require more differentiation than just a
single different final character! (We’ve used similar input values here to make the operation of the script
more clear.) In a real environment, we would expect the key fields to hold more diverse data to make
this algorithm useful. Nevertheless, the program shows how simple it is to perform useful operations
with the bit manipulation and conversion functions. We’ve implemented a simple algorithm to create
keys out of arbitrary character strings with just a few lines of code.
Summary
Rexx guarantees that the results of arithmetic operations will be the same regardless of the platform or
the Rexx interpreter. This is an important advantage over many other programming languages, which
place this burden on the developer. It makes Rexx code more reliable and portable with little effort on
the programmer’s part.
The only differences in calculations come in where implementations support different maximums (for
example, different maximum precision) or when they have differing amounts of total memory with
which to work.
107
Chapter 7
One downside to Rexx’s approach to numeric computations is its relatively slow speed. All variables
contain strings values that must be converted internally prior to computation. The result is that compu
tations are slower than they are in languages that carry numeric values in internal formats optimized to
perform calculations. Given modern computer hardware, this downside only matters when programs
are computationally bound. For the typical program, this “downside” matters not at all.
Rexx transparently handles issues with numeric conversions as necessary to perform numeric opera
tions. Nevertheless, there are times when knowing a little more about how Rexx handles numbers is use
ful; this chapter provides that detail. We discussed ways to represent numbers in Rexx variables, how to
control the precision to which calculations are carried out, techniques to format numbers for display, the
use of exponential notation, and the built-in functions that manipulate numbers.
The datatype function is the basic means by which the kinds of data held within variables may be
ascertained. Rexx provides a full set of functions for converting strings between data types. These are
usually referred to as the conversion functions. Appendix C provides a full coding reference for all Rexx
functions, including the conversion functions.
2. What’s the difference between scientific and engineering exponential notations? To which does
Rexx default? How do you display and/or change the default?
3. What functions are used to right-justify numeric output in reports?
4. What kinds of data conditions does the datatype function help identify?
5. Which of the following are valid numbers?
-22
‘ -22 ‘
2.2.
2.2.2
222b2
2.34e+13
123.E -2
123.2 E + 7
108
Subroutines, Functions, and
Modularity
Overview
Rexx fully supports structured programming. It encourages modularity—breaking up large, com
plex programs into a set of small, simple, interacting components or pieces. These components fea
ture well-defined interfaces that render their interaction clear. Modularity underlies good program
structure. Modularity means more easily understood and maintained programs than ill-designed
"spaghetti" code, which can quickly become unmaintainable on large programming projects.
Structured programming practices and modularity together reduce error rates and produce more
reliable code.
Rexx provides the full range of techniques to invoke other programs and to create subroutines and
functions. The basic concept is that there should be ways to link together any code you create, buy,
or reuse. This is one of the fundamental advantages to using a "glue" language like Rexx.
With Rexx, you can develop large, modular programs that invoke routines written in Rexx or other
languages, which issue operating system commands and utilize functions packaged in external func
tion libraries. This chapter describes the basic ways in which one writes modular Rexx programs.
This chapter investigates how to write internal subroutines and functions, and how to call them
from within the main program. Passing arguments or values into subroutines is an important
issue, as is the ability to pass changed values back to the calling program. Variable scoping refers to
the span of code from within which variables can be changed. This chapter explores the rules of
scoping and how they affect the manner in which scripts are coded. Finally, we introduce the idea
of recursion, a routine that calls itself as its own subroutine. While this may at first seem confusing,
in fact it is a simple technique that clearly expresses certain kinds of algorithms. Not all program
ming languages support recursion; Rexx does. The chapter includes a brief script that illustrates
how recursion operates.
Chapter 8
- External Programs
Internal routines are classified as either functionsor subroutines. Functions include those that are pro
vided as part of the Rexx language (the built-in functions) and those that you write yourself (user-defined
functions). Functions are distinct from subroutines in that functions mustreturn a single result string to
the caller through the return instruction with which they end. Rexx replaces the function code in any
statement with the returned value from the function. Subroutines may or may not send back a value to
their caller via their return instruction. The returned value from a subroutine, if there is one, is placed
into the special variable named result.
External routines can be functions, too. Often, these come in the form of a package designed to support a
particular functionality and are called extensionsor function libraries. External routines might also be the
equivalent of internal subroutines, written in Rexx, except that they reside in a different file than that of
the caller.
Rexx makes it easy to invoke external programs from your script, regardless of the language in which
they are written. If the Rexx interpreter encounters a string in a script that does not correspond to its
instruction set, it evaluates that expression and then passes it to the operating system for execution. So, it
is simple to run operating system commands or other programs from a Rexx script. Chapter 14 illus
trates how to do this. One of Rexx’s great strengths is its role in issuing, controlling, and coordinating
operating system commands. It is also easy to direct commands to other outside “environments” such as
110
Subroutines, Functions, and Modularity
text editors or other tools. Rexx is called a macro languagebecause it is often used to provide programma
bility for various tools. For example, on mainframes Rexx is used as the macro language to program the
widely used editors, XEDIT and the ISPF Editor.
There are a large variety of Rexx extensions and packages. For example, the open-source Rexx/SQLpack-
age provides an interface to a variety of relational databases from within Rexx scripts. Other examples
include interfaces to curses, the text-screen control package; to RexxXML, for XML programming; to
ISAM, the indexed sequential access method; to TK andDW, for easy GUI programming; to gd, for
graphics images; RxSock, for TCP/IP sockets, and many other interfaces. Chapters 15 through 18 discuss
and demonstrate some of these free and open-source packages. Chapter 29 discusses a few of the many
interfaces to mainframe Rexx and how Rexx offers a high-level macro and interface language for main
frame interfaces and facilities. Appendix H lists several dozen of the many free and open-source inter
faces that are available and tells how to locate them for downloading.
Functions may be invoked in either of two ways. One method codes the function name, immediately fol
lowed by arguments, wherever one might encode an expression:
The function is resolved and the string it returns is plunked right into the expression where it was
coded. In this case, the assignment statement then moves that value to the variable returned_string.
Since you can code a function anywhere you can code an expression, nesting the function within an if
or do instruction is common:
Here the call to the function balanced_parentheses is nested within an if instruction to provide a
result for the comparison. After the function balanced_parentheses has been run, its result is plunked
right where it was encoded in the if instruction.
You can nest functions within functions, as shown in this return instruction from one of the sample
scripts we discuss later in this chapter:
return substr(string,length(string),1) || ,
reverse(substr(string,1,length(string)-1))
Recall that the comma is the line continuation character. So, both of these lines constitute a single statement.
This return instruction features a complex expression that returns a single character string result to the
caller. The first part of the expression nests the length function within the substr function; the second
part nests length within substr within reverse. Yikes! Nesting is very powerful, but for the sake of
clarity we don’t recommend getting too fancy with it. Deeply nested expressions may show cleverness
111
Chapter 8
but they become unintelligible if too complex. When complex code is developed for corporate, govern
mental, or educational institutions, the value of that code drops the moment the programmer who wrote
it leaves the organization.
The second basic way to invoke a function is through the call instruction:
For example, to duplicate the code we looked at earlier where the invocation of the balanced_paren-
theses routine was nested within an if statement, we could have alternatively coded:
The result string from the function is automatically placed into the special variable named result and
may be accessed from there.
Special variable result will be set to uninitialized if not set by a subroutine. In this case its value will be
its own name in capitals: result.
Subroutinesmay only be invoked by the call instruction. Encode this in the exact same manner as the
second method for invoking functions:
The special variable result contains a value ifthe subroutine passed back a value on its return instruc
tion. Otherwise result will be set to uninitialized (the value result). All uninitialized variables are
their own names set to uppercase, so use this test to see if result was not set:
if result = ‘RESULT’ then say ‘RESULT was not set by the subroutine.’
The built-in function symbol can also be used to see if any variable is uninitialized or whether it has
been assigned a value. It returns the character string var if a variable has a value or the string lit other
wise. We can apply it to see if result was assigned a value:
To summarize, here’s a code snippet that shows how to organize a main routine (or driver) and its sub
routine. The code shows that the call to the internal subroutine did not set special variable result:
call subroutine_name
if result = ‘RESULT’
then say ‘No RESULT was returned’
else say ‘A RESULT was returned’
if symbol(‘RESULT’) == ‘VAR’
then say ‘A RESULT was returned’
112
Subroutines, Functions, and Modularity
if symbol(‘RESULT’) == ‘LIT’
then say ‘No RESULT was returned’
exit 0
subroutine_name:
return
The return instruction ends the subroutine, but does not include an operand or string to send back to
the calling routine. The code snippet displays these messages when it returns from the subroutine:
Now change the last statement in the code, the return instruction in the subroutine, to something like
this:
return ‘result_string’
return 0
Either encoding means that the special variable result is set to the string returned. After invoking the
internal routine, the code snippet now displays:
When encoding subroutine(s) and/or functions after the main routine or driver, code an exit instruc
tion at the end of the code for the main routine. This prevents the flow of control from rolling right off
the end of the main routine and going into the subroutines.
Here is another example that is the exact same as that seen in the preceding example. However, we have
coded it incorrectly by commenting out the exit instruction that follows the main routine. We have also
added a statement inside the subroutine that displays the message: Subroutine has been entered.
call subroutine_name
if result = ‘RESULT’
then say ‘No RESULT was returned’
else say ‘A RESULT was returned’
if symbol(‘RESULT’) == ‘VAR’
then say ‘A RESULT was returned’
if symbol(‘RESULT’) == ‘LIT’
then say ‘No RESULT was returned’
113
Chapter 8
subroutine_name:
say ‘Subroutine has been entered’ /* new line of code */
return 0
This shows you must code an exit instruction at the end of the main routine if it is followed by one or
more subroutines or functions. The last line in the sample output shows that the subroutine was entered
incorrectly because an exit instruction was not coded at the end of the main routine. As with the sub
routine’s return instruction, it is optional whether or not to code a return string on the exit statement.
In the preceding example, the exit instruction passed a return code of 0 to the environment.
What if we place the code of subroutines priorto that of the main routine? Here we located the code of
the subroutine prior to the driver:
subroutine_name:
say ‘Subroutine has been entered’
return 0
call subroutine_name
if result = ‘RESULT’
then say ‘No RESULT was returned’
else say ‘A RESULT was returned’
if symbol(‘RESULT’) == ‘VAR’
then say ‘A RESULT was returned’
if symbol(‘RESULT’) == ‘LIT’
then say ‘No RESULT was returned’
exit 0
What happened was that Rexx starts at the top of the file and proceeds to interpret and execute the code,
line by line. Since the subroutine is first in the file, it executes first. Its instruction return 0 caused exit
from the program before we ever got to the main routine! Oops. Always place the code for any internal
subroutines or functions afterthe main routine or driver.
114
Subroutines, Functions, and Modularity
We’ll cover program structure in more detail later. For now, here are some basic rules of thumb:
We sso thst Rexx uninitislizes specisl vsrisble result ohen s cslled subroutine does not psss bsck s
result string. If vou ever need to uninitislized s Rexx vsrisble vourself, code the drop instruction:
drop my_variable
This sets s vsrisble vou msv hsve used bsck to its uninitislized stste. It is noo equsl to its oon nsme in
sll uppercsse.
Data passed into a script when it is invoked are called command-line arguments or inputparameerrs. To
invoke s Rexx script snd psss it commsnd-line srguments or psrsmeters, enter something like this:
The script reads these three input strings parameter_1, 2, and parameter_3 with the arg instruction.
arg automaticallv translates the input parms to uppercase. It is the equivalent of the instruction parse
upper arg. If no uppercase translation is desired, use parse arg. Remember that a period following
either of these instructions discards anv more variables than are encoded on the arg or parse arg
instruction. This example discards anv arguments bevond the third one, if anv are entered:
Here is the same example coded with the parse arg instruction:
115
Chapter 8
By default, the arg and parse arg instructions splice the input parameters into pieces based on their
separation by one or more intervening spaces. If you ran the program like this:
You’d want to code this statement in the script to pick up the input arguments:
input_1 = parameter_1
input_2 = 2
input_3 = parameter
input_4 = _3
As per the basic rules of parsing, encoding too many input parameters puts all the overflow either into
the placeholder variable(the period) or into the last specified input variable on the parse arg instruction.
Entering too few input parameters to match the parse arg statement means that the extra variables on
the parse arg will be set to uninitialized. As always, an uninitialized variable is equal to its own name
in uppercase.
Code a comma between each of the parameters in the call instruction. The string (if any) sent back
from the call will be available in the special variable named result.
Code a function call just like the call to the previous subroutine. Or encode it wherever you would an
expression, as illustrated earlier, in the form:
Inside the function or subroutine, use either arg or parse arg to retrieve the arguments. The function
or subroutine picking up the input parameters should encode commas that parallel those of the call in
its arg or parse arg instruction:
or
116
Subroutines, Functions, and Modularity
The period or placeholder variable is optional. Presumably, the subroutine or function knows how many
input parameters to expect and does not need it.
These examples illustrate the arg instructionretrieving the argument string passed to a script and splic
ing it apart into its individual pieces. There is also an arg built-in function. The arg function returns
information about input arguments to the routine. For scripts called as functions or subroutines, the arg
function either:
Let’s lllk ht h pel exhosles. Tl lehrn lll ohny hrgcoents lere shssee in, clee:
number_of_arguments = arg()
get_third_argument = arg(3)
Tl see ip tle tlirehrgcoent exists (lhs shssee lr encleee in tle chll), lrite:
lr
The first of the two sample lines show that an input argument reap by an internal routine will be the null
string ip it is nlt scssliee tl tle rlctine. Tlis eippers prlo h cloohne-line insct hrgcoent tlht is rehe
but not suppliep, which is set to uninitializep (its own name in uppercase).
The seconp sample line shows one of the two options that can be usep with the arg function:
The arg function onlysupplies this information for scripts that are callep as functions or subroutines. For
scripts invokep from the operating system’s commanp line, the arg function will always show only 0 or
1 argument strings. In this respect Rexx scripts invokep as commanps from the operating system behave
pifferently than scripts invokep as internal routines (functions or subroutines). This is one of the very
few Rexx inconsistencies you’ll have to remember: the arg function tells how many arguments are
passep into an internal routine, but appliep to the commanp-line arguments coming into a script, it
always returns either 0 or 1.
117
Chapter 8
A Sample Program
To see how parameters are passed into programs, and how code can be modularized, let’s look at a cou
ple sample programs. The first sample program consists of a brief script that reads information from the
command line. This main routine or “driver” then turns around and calls a subroutine that performs the
real work of the program. Then the driver displays the result from the subroutine on the user’s screen.
Of course, the driver could actually be part of a larger application. For example, it might be a “service
routine” shared among programs in the application. Whatever its use, the important principles to grasp
are how code can be modularized and how information can be passed between modules.
The first sample program tells whether parentheses in a string are balanced. A string is said to be bal
anced if:
Hern are come examples. Thncn input strings meet the two criteria and co are soncidnrnp balanced:
(())
() () ()
return (qiu(slk) ())
(((((()())))))
if (substr(length(string,1,2)))
Thece are unbalanced ctringc. Either the numberc of left and right parenthecec are unequal, or a right
parenthecic occurcprior to itc correcponding left parenthecic:
The lact example chowc that a ccript like thic could be uceful ac a cyntax-checker, or ac a module in a
language interpreter. You san actually uce it to verify that your csriptc poccecc properly ensodep, bal
anced cetc of parenthecec.
To run the program, enter the ctring to verify ac a sommand-line argument. Recultc appear on the next
line:
Try again, thic time adding one lact right parenthecic to the input ctring:
Here’c the sode for the saller. All it doec ic read the ucer’c sommand-line input parameter and pacc that
sharaster ctring to a funstion named balanced_parens that doec the work. The funstion
balanced_parens may be either internal or external — no shange ic required to itc soding regardlecc of
118
Subroutines, Functions, and Modularity
where you place it. (However, you must be sure the operating system knows where to locate external
functions. This often requires setting an environmental variable or the operating system’s search path for
called routines. We’ll discuss this in detail later.)
/* CALL BAL: */
/* */
/* Determines if the parentheses in a string are balanced. */
exit 0
Here’s the internal or external function that figures out if the parentheses are balanced. The algorithm
keeps track of the parentheses simply by adding 1 to a counter for any left parenthesis it encounters, and
subtracting 1 from that counter for any right parenthesis it reads. A final counter (ctr) equal to 0 means
the parentheses are balanced—that there are an equal number of left and right parentheses in the input
string. If at any time the counter goes negative, this indicates that a right parenthesis was found prior to
any possible matching left parenthesis. This represents another case in which the input string is invalid.
/* BALANCED PARENS: */
/* */
/* Returns Y if parentheses in input string are balanced, */
/* N if they are not balanced. */
balanced_parens:
Another way to code this problem is for the subroutine to return 1 for a string with balanced parenthe
ses, and 0 if they are unbalanced. Then you could code this in the caller:
if balanced_parens(string) then
say ‘Parentheses are balanced!’
else
say ‘Parentheses are NOT balanced’
119
Chapter 8
This allows coding the function as an operatorless condition test in a manner popular in programming in
languages like C, C++, or C#. But remember that the expression in an if instruction must evaluate to 1
(TRUE) or 0 (FALSE) in Rexx, so the function mustreturn one of these two values. Anonzero, positive
integer other than 1 will not work in Rexx, unlike languages in the C family. A positive value other than
1 results in a syntax error in Rexx (we note, though, that there are a few Rexx interpreters that are
extended to allow safe coding of operatorless condition tests).
Coding operatorless condition tests also runs counter to the general principle that a function or subrou
tine returns 0 for success and 1 for failure. Wouldn’t balanced parentheses be considered “success”? This
coding works fine but contravenes the informal coding convention.
This issue is common to many programming languages and is called the function search order. In Rexx the
function search order is:
Where Rexx looks for external functions is operating-system-dependent. You can normally place exter
nal functions in the same directory as the caller and Rexx will find them. On many platforms, you must
set an environmental variableor a search pathparameter to tell the operating system where to look for
external functions and subroutines.
The function search order means that you could code an internal function with the same name as a Rexx
built-in function and Rexx will use yourfunction. You can thus replace, or override, Rexx’s built-in
functions.
If you want to avoid this, code the function reference as an uppercase string in quotation marks. The
quotation marks mean Rexx skips Step 1 and onlylooks for built-in or external functions. Uppercase is
important because built-in functions have uppercase names.
With this knowledge, you can override Rexx functions with your own, while still invoking the built-in
functions when you like. You can manage Rexx’s search order to get the best of both worlds.
120
Subroutines, Functions, and Modularity
Recursion
Arecursivefunction or routine is one that calls itself. Any recursive function could be coded in traditional
nonrecursive fashion (or iteratively), but sometimes recursion offers a better problem solution. Not all
programming languages support recursion; Rexx does.
Since a recursive function invokes itself, there must be some end test by which the routine knows to stop
recursing(invoking itself). If there is no such end test, the program recurses forever, and you have effec
tively coded an “endless loop!”
Figure 8-2
This sample recursive function reverses the characters within a given string—just like Rexx’s reverse
built-in function. If you feed it the character string abc, it returns the string cba.
The function calls itself to process each character in the input string and finds its “end test” when there
are no more characters left in the string to process. Each time the function is entered, it returns the last
character in the string and recurses to process the remaining string.
/* REVERSE: */
/* */
/* Recursive routine that reverses the characters in a string. */
reverse: procedure
121
Chapter 8
then return ‘’
else
return substr(string,length(string),1) || ,
reverse(substr(string,1,length(string)-1))
The reverse function uses the strictly equaloperator ( == ). This is required because the regular “equals”
operator pads item with blanks for comparisons, something that might not work in this function. The
line that uses the strictly equal operator compares the input string to the null string, the string that con
tains no characters, represented by two back-to-back quotation marks ( ‘’ ). This is the “end test” that
tells the function to return, because it has processed all the characters in the original input string:
The last two lines of the function show how to continue a statement across lines. Just code a comma (,)
and the return instruction’s expression spans into the next line. The comma is Rexx’s line continuation
character. Code it at any natural breakpoint in the statement. Between parts of a statement is fine; within
the middle of a character string literal would not work. This is valid:
say ‘Hi ‘ ,
‘there!’ /* valid line continuation */
But this will fail with a syntax error, because the line continuation character appears in the middle of a
quoted literal:
say ‘Hi ,
there!’ /* invalid line continuation, syntax error! */
Of course, the trick to this program to reverse character strings is this one, heavily nested line of code:
return substr(string,length(string),1) || ,
reverse(substr(string,1,length(string)-1))
The first portion of this statement always returns the last character in the substring being inspected:
substr(string,length(string),1)
An alternative way to code this is to use the right function, as in: right(string, 1).
The second portion of the return statement recursively invokes the reverse function with the remain
ing substring to process. This is the original string passed in, minus the last character (which was just
returned to the caller):
reverse(substr(string,1,length(string)-1))
To test a program like this, you need a simple driveror some “scaffolding” to initially invoke the new
reverse function. Fortunately, the rapid development that Rexx enables makes this easy. Coding a
driver to test the new reverse function is as simple as coding these few lines:
122
Subroutines, Functions, and Modularity
This code reads an input string from the user as an input command-line argument. It invokes the recur
sive, user-written reverse function and displays the result to the user.
The say instruction in this code uses the special variable result to display the string returned from the
reverse function on the user’s display screen:
Our new reverse function has the same name and functionality as Rexx’s own, built-in reverse func
tion. Which will Rexx run? The function search order tells us. Assuming that the reverse function we
coded is internal, Rexx invokes it, because user-written internal functions have priority over Rexx’s
built-in functions in the function search order. If we want to use the built-in Rexx reverse function
instead, we would code the name of the function in quoted uppercase letters. These two lines show the
difference. This line invokes our own reverse function:
More on Scoping
Developers place internal functions and subroutines after the main routine or driver in the script file.
Here’s the basic prototype for script structure where the main script has subroutines and/or functions:
main_routine:
call my_function parameter_in
call my_subroutine parameter_in
exit 0
my_function: procedure
return result_string
my_subroutine: procedure
return
Rexx does not require any label for the main routine or driving portion of the script, but we recommend
it as a good programming practice. A Rexx labelis simply a name terminated with a colon. In this script,
we’ve identified the driver routine with the label main_routine: . This is good programming practice
in very large programs because it may not always be obvious where the logic of the driver really starts.
In other words, if there is a long list of variable declarations or lots of initialization at the top of a script,
identifying where the “real” work of the main routine begins can sometimes be helpful.
123
Chapter 8
A key issue in any large program is scoping— which of the caller’s variables are available for reading
and/or updating by a called function or subroutine. In Rexx, the procedure instruction is the basic tool
for managing variable scoping. procedure is encoded as the first instruction following the label in any
function or subroutine for which it’s used.
The procedure instruction protects all existing variables by making them unknown to any instructions
that follow. It ensures that the subroutine or function for which it is encoded cannot access or change
any of its caller’s variables. For example, in the reverse function, we coded this first line:
reverse: procedure
This means the reverse routine cannot read or update any variables from its caller — they are protected
by the procedure instruction. This is a good start on proper modularity, but of course, we need a way to
give the reverse routine access to those variables it doesneed to access. One approach is to pass them in
as arguments or parameters, as we did in calling the reverse function, with this general structure:
calling routine:
parse arg parm_1 parm_2 . /* get command-line arguments from the user */
call function_name parm_1, parm_2 /* pass them to the internal routine */
say ‘The function result is:’ result /* retrieve RESULT from the routine */
exit 0
function_name: procedure
parse arg parm_1, parm_2 /* get parameters from the caller */
return result_string /* return result to caller */
The procedure instruction protects all variables from the function or subroutine. This function cannot
even read any of the caller’s variables. It knows only about those passed in as input parameters, parm_1
and parm_2. It can read the variables that are passed in via arg, and it sends back oneresult string via
the return instruction. It cannot change the value of any of the arg variables in the caller. These are passed in
on a read-only basis to the function or subroutine, which can only pass back one string value by a
return instruction.
Another approach to passing data items between routines is to specify exposed variableson the proce
dure instruction. These variables are available for both reading and updatingby the invoked routine:
In this case the function or subroutine can read and manipulate the variable variable_1 and the spe
cific array element array_element.1. The function or subroutine has full read and update access to
these two expose’d variables.
With this knowledge, here’s an alternative way to structure the relationship between caller and called
routine:
calling_routine:
parse arg parm_1 parm_2 . /* get command-line arguments from the user */
call subroutine_name /* call the subroutine (or function) */
say ‘The function result is:’ result /* retrieve RESULT from the routine */
say ‘The changed variables are:’ parm_1 parm_2 /* see if variables changed */
124
Subroutines, Functions, and Modularity
exit 0
The output from this code demonstrates that the subroutine changed the values the caller originally set
for variables parm_1 and parm_2:
The procedure instruction limits variable access in the called function or subroutine. Only those vari
ables specifically named on the procedure expose instruction will be available to the called routine.
To summarize, there are two basic approaches to making caller variables available to the called routine.
Either pass them in as input arguments, or code the procedure expose instruction followed by a vari
able list. The called function or subroutine cannot change input arguments—these are read-only values
passed by the caller. In contrast, any variables listed on the procedure expose statement can be both
read and updated by the called function or subroutine. The calling routine will, of course, “see” those
updated variable values.
Two brief scripts illustrate these principles. This first demonstrates that the called routine is unable to
change any variables owned by its caller because of the procedure instruction coded on the first line of
the called routine:
calling_routine:
variable_1 = ‘main’
variable_2 = ‘main’
call my_subrtn(variable_1)
my_subrtn: procedure
variable_1 = ‘my_subrtn’
125
Chapter 8
variable_2 = ‘my_subrtn’
The first output line shows that the subroutine was passed a value for variable_1, but variable_2
was not passed in to it. The subroutine accessed the single value passed in to it by its arg instruction.
The second line of the output shows that the called routine locally changed the values of variables
variable_1 and variable_2 to the string value my_subrtn —but the last line shows that these assign
ments did not affect the variables of the same names in the caller. The subroutine could not change the
caller’s values for these two variables. This is so because the procedure instruction was encoded on the
subroutine but it did not list any variables as expose’d.
This next script is similar but illustrates coding the procedure expose instruction to allow a called rou
tine to manipulate the enumerated variables of its caller:
/* This code shows that ONLY those variables listed after EXPOSE */
/* may be read and updated by the called function or subroutine. */
calling_routine:
variable_1 = ‘main’
array_name. = ‘main’ /* The called routine can update */
array_element.1 = ‘main’ /* array elements if desired. */
not_exposed = ‘main’
126
Subroutines, Functions, and Modularity
The first output line shows that the subroutine accessed the three caller’s variables listed on the proce
dure expose instruction. This shows the three variables set to the string value main. The fourth vari
able shows up as not_exposed because the subroutine did not list it in its procedure expose
statement and cannot access it.
The second output line shows that the subroutine set the value of the three variables it can change to the
value my_subrtn. This line was displayed from within the subroutine.
The last output line confirms that the three variables set by the subroutine were successfully passed back
to and picked up by the caller. Since only three variables were passed to the subroutine, the fourth vari
able, originally set to the string value main by the caller, still retains that same value.
What about external routines? Invoke them just like internal routines, but the Rexx interpreter always
assigns them an implicit procedure instruction so that all the caller’s variables are hidden. You cannot
code a procedure expose instruction at the start of the external routine. Pass information into the exter
nal routine through input arguments. Code a return instruction to return a string from the external rou
tine. Or, you can code an exit instruction with a return value.
For internal routines, if you code them without the procedure instruction, all the caller’s variables are
available to the internal routines. All the caller’s variables are effectively global variables. Global variables
are values that can be changed from any internal routine. Global variables present an alternative to pass
ing updatable values into subroutines and functions via the procedure expose instruction.
Developers sometimes like using global variables because coding can be faster and more convenient.
One does not have to take the time to consider and encode the correct procedure expose instructions.
But global variables are not considered a good programming practice because they violate one of the key
principles of modularity—that variables are explicitly assigned for use in specific modules. So that you
recognize this scenario when you have to maintain someone else’scode, here is the general script structure
for using global variables:
my_subroutine:
/* all variables from MAIN_ROUTINE are available to this routine for
read and or update */
a = ‘this setting will be seen by the caller’
return
my_function:
/* all variables from MAIN_ROUTINE are available to this routine for read
and or update */
a = ‘this new value will be seen by the caller’
return 0
127
Chapter 8
The program output shows that the two internal routines are able to change any variable values in the
calling routine at will. The two output lines are displayed by the driver. The latter portion of each line
shows that the subroutine and function were able to change the value of the global variable named a:
Prove subroutine changed the value: this setting will be seen by the caller
Prove the function changed the value: this new value will be seen by the caller
All you have to do to use global variables is neglect to code the procedure instruction on subroutines
on functions. This is convenient for the developer. But in large programs, it can be extremely difficult to
track all the places in which variables are altered. Side effectsare a real possibility, unexpected problems
resulting from maintenance to code that does not follow the principles of structured programming and
modularity.
To this point, we’ve discussed several ways to pass variables into and back from functions and subrou
tines. This chart summarizes the ways to pass information to and from called internal subroutines and
functions:
Whichever approach(es) you use, consistency is a virtue. This is especially the case for larger or more
complex programming applications.
128
Subroutines, Functions, and Modularity
This program searches a string and returns the rightmost occurrence of a specified character. It is a recur
sive function that duplicates functionality found in the built-in lastpos function. It shows how to pass
data items to a called internal routine as input parameters and how to use the procedure expose
instruction to pass in updateable items.
/* RINDEX: */
/* */
/* Returns the rightmost position of a byte within a string. */
This script requires two inputs: a character string to inspect for the rightmost occurrence of a character,
and the character or “search byte” to look for.
When invoked, the function looks to see if the last character in the string to search is the search character.
If yes, it returns that position:
If the search character is not found, the routine calls itself with the remaining characters to search as the
new string to search:
new_string_to_search = substr(string,1,string_length_1)
return rindex(new_string_to_search)
The end condition for recursion occurs when either the character has been found, or there are no more
characters in the original string to search.
129
Chapter 8
The function requires two pieces of input information: the string to inspect, and the character to find
within that string. It reads the string to inspect as an input parameter, from the parse arg instruction:
The first line in the function gives the program access to the character to locate in the string:
The two pieces of information are coming into this program in two different ways. In a way this makes
sense, because the character to locate never changes (it is a global constant), but the string that the func
tion searches is reduced by one character in each recursive invocation of this function. While this pro
gram works fine, it suggests that passing in information through different mechanisms could be
confusing. This is especially the case when a large number of variables are involved.
For large programs, consistency in parameter passing is beneficial. Large programs become complicated
when programmers mix internal routines that have procedure expose instructions with routines that
do not include this instruction. Rexx allows this but we do not recommend it. Consistency underlies
readable, maintainable code. Coding a procedure or procedure expose instruction for everyinternal
routine conforms to best programming practice.
Summary
This chapter describes the basic mechanisms by which Rexx scripts are modularized. Modularity is a
fundamental means by which large programs are rendered readable, reliable, and maintainable.
Modularity means breaking up large, complex tasks into a series of smaller, discrete modules. The inter
faces between modules (the variables passed between them) should be well defined and controlled to
reduce complexity and error.
We covered the various ways to pass information into internal routines and how to pass information
from those routines back to the caller. These included passing data elements as input arguments, the
procedure instruction and its expose keyword, and using global variables. We discussed some of the
advantages and disadvantages of the methods, and offered sample scripts to illustrate each approach.
The first sample script read a command-line argument from its environment and passed this string as an
input argument to its subroutine. The subroutine passed a single value back up to its caller by using the
return instruction. The last sample script was recursive. It invoked itself as a subroutine and illustrated
how the procedure expose instruction could be used to pass values in recursive code. This latter
example also suggests that consistently encoding the procedure expose instruction on everyroutine is
a good approach for large programming projects. This consistent approach reduces errors, especially
those that might otherwise result from maintenance on large programs that use global variables.
130
Subroutines, Functions, and Modularity
131
n
Rexx offers tremendous power in its tracing facility. Implemented by its trace instruction, the
trace built-in function, and a variety of supporting functions and features, the tracing facility
enables you to quickly and easily step through your code as it executes. Rexx will display the
results of expression evaluation, variable contents, lines of code as they are translated and run,
program position . . . indeed, almost anything going on in the script. You can single-step through
your code, allowing Rexx to pause before or after each line of the source code. You can execute
Rexx statements while your script is paused, for example, to inspect or alter the values of vari
ables. At anytime, you can easily turn tracing on, off or to some different level of granularity. The
trace facility makes debugging even the most complex logic a simple affair. This chapter describes
the trace facility and how to use it in detail.
Debugging Options
Let’s start with the most basic approach to debugging. This simple technique temporarily adds extra say
statements to the code to display variable values. Rexx makes this easy because of the manner in which
the say instruction automatically concatenates expressions.
Take as an example the rindex program in the previous chapter. Recall that this script returns the right
most position of a given character within a string. When first written and run, this program displayed
this output as its answer regardless of the input search string:
Clearly, something was wrong. Simply adding one line with a say instruction at the start of the routine
made the problem evident:
When the program ran with this debugging aid, here were the results from the say instruction:
The value of the byte to search for, entered in the command line as the character b, was not being picked
up by the routine. Instead of the character string search_byte, we should have seen the input parame
ter string b repeated on each output line.
134
Debugging and the Trace Facility
After adding the expose search_byte keywords to the procedure instruction, the program result
was what we would expect:
So, the problem was improperly passing a value to a subroutine. The say instruction is ideal for this
quick debugging because it automatically concatenates operands for instant output.
trace [setting]
135
Chapter 9
When is each of these trace settings most useful? This setting is the default:
trace n
It traces nothing except failed host commands. It is minimally intrusive and is a good default value for
working programs.
trace r is recommended for general-purpose debugging. It traces clauses before they execute and the
final results of expression evaluation. It also shows when values change by pull, arg, and parse
instructions. When you need to run a trace, trace r is usually where to start. If problems persist, trace
i gives fuller detail. It gives everything that trace r does plus includes the details of intermediate
expression evaluation.
If you’re unsure about what routines are being entered and executed, try trace l. This lists all labels pro
gram execution passes through and shows which internal routines are entered and run. It’s an easy way
to determine if a subroutine or function you coded is being entered and executed at the proper time.
If the problem is that commands to the host operating system are failing, trace c will trace all host
commands before their execution. For any that cause an error or fail, it also shows the return code from
the command. trace e and trace f are weaker forms of trace c that trace host command errors and
failures, respectively. We recommend trace c as simplest and most complete if problems are occurring
in executing host commands.
Where does one code the trace instruction? Anywhere in the code you like. A simple approach is to
code one trace instruction near the very top of the program. It can be set to trace n (the default), and
then changed to any other value desired during debugging. Just remember to set it back to trace n
once debugging is completed. This approach is simple and consistent but does require changing the
code to change the trace setting.
Another approach is to code a trace instruction at the start of the program, then have the program read
the trace option dynamically, from the outside environment. For example, the program could prompt
the user to enter the trace setting. Or, it might accept it as an optionally coded command-line argument
to the program. Or, the program could even read this information from a “control file” or configuration
file that dictates program behavior. For example, under Windows you could use an .ini file to config
ure tracing. Under Unix or Linux, you might use a config file.
However you set the trace option, you can code as many trace instructions as you like in a single script.
These can flip the trace off or on, or set different levels of trace detail appropriate to different routines or
sections of code. Scripts can dynamically control their trace levels themselves.
The trace instruction accepts the setting as a constant (a string literal or symbol), or it can be encoded
as a variable or even as an expression to evaluate with the optional value keyword. These are the two
basic formats for the trace instruction:
trace [setting]
or
136
Debugging and the Trace Facility
trace_variable = ‘r’
trace value trace_variable
The trace instruction can be coded multiple times in a program, and you can turn tracing on or off (by
trace o) as desired. This is mainly of use in very large programs, where you really want to zero in only
on problems occurring in a newly added routine or better understand the effects of newly changed or
problem code.
Let’s look at some examples. Here is the output that results from placing a trace r instruction in the
rindex function immediately after the 1st line of code (after the procedure instruction):
D:\Regina\hf>regina rindex.rexx ab b
16 *-* parse arg string /* read the string */
>>> “ab”
18 *-* say string search_byte /* show recursive trace for fun */
>V> “ab”
>V> “b”
ab b
20 *-* string_length = length(string) /* determine string length */
>V> “ab”
21 *-* string_length_1 = length(string) -1 /* determined string length - 1 */
>V> “ab”
23 *-* if string == ‘’ /* Here’s the ‘end recursion’ condition. */
>V> “ab”
25 *-* do
26 *-* if substr(string,string_length,1) == search_byte then
>V> “ab”
>V> “2”
>V> “b”
27 *-* return string_length
>V> “2”
The rightmost byte position is: 2
With trace r, line 16 shows how the parse arg instruction assigns values on entry to this function.
Every expression appears priorto execution, then the result to which it evaluates. The listing shows that
this trace setting resolves most debugging needs handily.
If you continue to have trouble, change the trace r to trace i and see the intermediate results of
expression evaluation as well. This makes for longer, more complex output that you’ll want to see only if
you’re having real trouble in debugging.
Let’s take out the trace instruction in the rindex function, and instead place a single trace l at the
top of the driver program. This traces all the labels the script execution passes. In this case, it verifies that
the driver routine invokes the rindex function:
137
Chapter 9
The trace l label trace is great for automatically displaying which internal routines are called during a
large program. It gives more concise output than trace r when you’re just worried about which rou
tines are being called and when. Use it for those situations in which you’re not sure if a routine is being
called or if it is not clear how often a routine is invoked.
To debug scripts that issue operating system commands, trace c is a good choice. It traces host com
mands priorto execution, and it gives the return code from any command that results in error or failure.
To check its output, here’s a simple test program that we ran under Windows. This program intends to
issue the dir (list directory) command to Windows, but the command was misspelled as dri:
3 *-* ‘dri’
‘dri’ is not recognized as an internal or external command,
operable program or batch file.
+++ RC=1 +++
The output clearly shows the problem with the operating system command that was issued.
trace e shows any host command that results in error or failure and its return code, while trace f
shows host commands that result in failure and their return code. We recommend trace c because it
always lists the command that caused the problem.
The trace output on the second line shows that this string was assigned to the variable string as a
result of the parse arg instruction.
When the trace shows the contents of a variable, it precedes with this symbol >V>. These two statements
show that the string sent back via the return instruction is “2”:
Notice how the trace is indented to convey more information. The strings displayed on the screen by the
say instruction start on the left, while program statements and variable contents are indented. This
138
Debugging and the Trace Facility
sample code is pretty linear, but where nesting is more involved, indentation makes the trace output
much easier to follow.
For many programs, just these simple rules are all that is required to interpret trace output. Here are the
trace output identifiers you might encounter:
When coded without an input parameter, the trace function returns the current trace setting:
139
Chapter 9
The allowable values for the trace function are the exact same as those for the trace instruction. The
following table lists the possible trace function values. Since these values are the same as those for the
trace instruction, you can review the trace instruction table near the beginning of this chapter for a
full explanation of each setting. This table lists the one-word meanings of the options for easy recall:
A All
C Commands
E Errors
F Failure
I Intermediates
L Labels
N Normal
O Off
R Results
When the trace function includes an operand, it returns the current trace setting; thenit alters the
trace level to the new setting. Look at these three instructions run in sequence:
Interactive Tracing
So far we have discussed the trace setting as if it is something one turns on or off (multiple times if
desired). Then you read its output after the script executes. This is a “batch” approach to debugging. In
fact, one of the biggest benefits of tracing is the potential to pause the script at desired points and per
form real-time operations, called interactive tracing.
To start interactive tracing, code the trace instruction with a question mark ( ? ) preceding its argu
ment. For example, this statement starts interactive tracing for results:
Here’s another example. This statement turns interactive tracing on for commands:
The ? is a toggle switch. If tracing is off, this it turns on; if tracing is on, this turns it off. The first trace
instruction or function you execute with ? encoded turns tracing on. The next one that executes with the
question mark will turn it off.
140
Debugging and the Trace Facility
When in interactive mode, the Rexx interpreter pauses after each statement or clause. Or, to be more pre
cise, the Rexx interpreter pauses after executing each statement and displays the next statement to exe
cute. At this point, you can perform any one of three actions listed in the following table:
Press the <enter> key (also referred The interpreter continues until the next
to as entering a null line) point at which it should pause.
Enter an equals sign = The interpreter reexecutes the last
clause. This allows you to “go back” one
clause, make changes, and allow it to be
rerun. For example, you could alter the
value of a variable or change how an if
instruction might be evaluated.
Enter any Rexx clause or expression or statement Rexx immediately executes what you’ve
entered.
The last option listed in this table bears some explanation. When the interpreter pauses, you can enter
any valid Rexx clause, expression, or statement. The interpreter then immediately executes what you’ve
entered. This allows you to change the value of variables to see how the program will respond. For
example, you could enter an executable statement like this to alter a variable’s value and see how this
alters the script’s execution:
my_variable = ‘123’
As another example, you could enter statements to display the contents of an array. This would allow
you to verify that the array contains what you think it should at that point in your program:
You can even enter a statement to change the level of detail in the trace output, or you can run any other
valid Rexx statement.
Interactive tracing lets you single-stepthrough a script, inspecting and then running that code one clause
at a time. You can inspect or change variables at will, see how code changes would affect execution,
change various aspects of the environment, and alter the trace level itself.
Settings for the trace instruction are saved and restored across internal routines. If you enter an internal
routine of no interest, merely turn off the trace:
The original trace setting will be restored when the caller is reentered.
Trace options are one of the few places in which Rexx uses abbreviations. Normally, the language uses
full words for enhanced readability. The reason the trace instruction is an exception is that interactive
tracing allows terse input from the keyboard so that the developer does not have to type so much and
can work with simple mnemonic abbreviations when debugging.
141
Chapter 9
Sometimes during a trace, its useful to be able to “skip ahead” a number of clauses with the interactive
trace temporarily turned off. To do this, code a negative number on the trace instruction. This tells the
interpreter to skip tracing a certain number of clauses, and then to resume as before. For example, this
statement skips tracing the next 50 clauses, after which tracing resumes:
trace -50
You can also code a positive number on the trace instruction to skip a specified number of interactive
pauses. For example, this instruction skips the next five interactive pauses, then resumes tracing as
before:
trace 5
Some Rexx implementations allow turning on the trace externally, so you do not have to alter your script
to trace it. An example is mainframe Rexx, described in detail in Chapter 29. Mainframe Rexx under
operating systems such as VM and OS permits immediate commands, which can alter the trace level from
outside the script while the script executes. All standard Rexx interpreters support internally changing
the trace through the trace instruction and trace function.
Summary
As an interpreted scripting language, Rexx offers superior debugging facilities. Chief among them is
interactive tracing, by which you can dynamically inspect and even alter your script’s variables and its
execution.
In most cases a simple batch approach to turning on the trace quickly resolves any programming prob
lem. But when called for the full power of a completely interactive tracing facility is available. Using it,
there are very few logic and programming errors you cannot quickly rectify. The trace facility is a big
advantage of Rexx scripting versus programming in traditional compiled programming languages.
Interacting tracing can dramatically reduce the time spent in debugging and resolving logic errors.
142
10
Errors and Condition
Trapping
Overview
Full-featured programming languages require a mechanism through which programmers can
catch, or trap, exceptions or errors. In Rexx, this is often referred to as exception handling or condttion
trapping.
Rexx offers a simple, but serviceable, means for trapping exceptional conditions. When an exception
occurs, control is transferred to a routine to address the error. After the error routine handles the
condition, execution of the primary script can resume.
This chapter explores Rexx's exception-trapping mechanism and the way in which you can use it
in scripts to identify, capture, and manage errors. First, we'll discuss the specific kinds of errors
that Rexx can trap. We'll discuss how to set up, or enable, error trapping. Then we'll take a look at
a program that illustrates the exception-trapping mechanism. We'll progressively improve this
program to expand its error-trapping capabilities and demonstrate different approaches to manag
ing errors. We conclude by mentioning some of the limitations of exception conditions in standard
Rexx, and how some Rexx interpreters extend beyond the ANSI standards to provide more gener
alized error trapping.
Error Trapping
When an error condition occurs, it is considered to be raised. Rexx interpreters that adhere to the
TRL-2 standard allow the raising of six different error conditions, while the ANSI-1996 standard
adds a seventh error condition. The table below lists all the error conditions:
Chapter 10
If the signal or call statement does not the specify the name of the error routine to which to transfer
control, by default Rexx transfers control to a routine with the same name as that of the error. For exam
ple, say you code:
signal on novalue
This statement enables the novalue error condition without specifically naming the error routine to han
dle it, so Rexx assumes that it will find an error routine named novalue to handle the condition.
Here’s the basic coding template for how to enable and code for error conditions:
main_routine:
144
Errors and Condition Trapping
exit
novalue_handler:
Figure 10-1 shows the basic logic of conditions or error handling diagrammatically.
Condition Trapping
Remember that we previously saw the signal instruction used in a manner similar to an unconditional
goto statement. This is a new form of the signal statement that sets up and enables a condition trap.
In the sample code, this line enables the trap for the novalue condition and names the routine that will
handle this error:
This line does notimmediately transfer control to the routine to which it refers; it only enablesthe trap so
that the error routine will be invoked when and if the exception condition named on the signal state
ment (the novalue condition) occurs.
The name keyword is followed by the name of the error routine for that condition. In this example,
the name of the routine that will be run when the novalue condition occurs is novalue_handler.
Somewhere later in the code there mustbe a label that identifies the routine that handles the error condi
tion. This error-handling code performs any appropriate processing for the specified condition. In most
cases, error routines return control to the point of invocation after printing an error message or perform
ing other corrective action. But they could end the script or take any other action desired.
145
Chapter 10
The signal or call instructions can be coded without explicitly specifying the name of the trap routine. In
this case, the label of the error routine must be the same as the condition that is raised. Here are examples:
We’ll get into the differences between signal on and call on later. For now, note that signal can
be coded with all seven conditions but that call cannot be coded with the syntax, novalue, and
lostdigits errors. call enables a smaller set of error-condition routines.
A Sample Program
Here’s a simple script that illustrates how to trap syntax or runtime errors. The program prompts the user
to enter a Rexx expression or statement. Then it executes the interpret instruction to evaluate that
expression and execute it. The prompt/interpret loop continues indefinitely until the user enters the let
ters exit. At that point the interpret instruction executes the exit instruction the user entered, which
terminates the program.
Besides showing how to trap an error condition, this is a useful script because it allows you to interac
tively test various Rexx statements. You can purposely enter a statement with invalid syntax and read
the error message with which Rexx responds. The script provides a handy “statement tester.” It also
shows how the interpret instruction can be used to dynamically interpret and execute Rexx state
ments. Here’s the script:
/* REXX TRY1 */
/* */
/* Reads user-input Rexx statements and interprets them. */
start_loop:
signal on syntax /* establish error trap */
do forever
call charout ,”==> “ /* prompt/read on 1 line */
parse pull expression$
interpret expression$ /* INTERPRET user’s input */
end
end_start_loop: exit 0
SYNTAX:
say ‘SYNTAX:’ errortext(rc) ‘(error’ rc’)’ /* write error*/
signal start_loop /* return to processing */
C:\Regina\pgms>regina rexx_try1.rexx
Type: ‘exit’ to end this program
==> say 3+2
146
Errors and Condition Trapping
5
==> if a=3 then
SYNTAX: Incomplete DO/SELECT/IF (error 14)
==> if a=3
SYNTAX: THEN expected (error 18)
==> exit
The sample interaction shows that the user starts the program and it prompts him or her to enter a Rexx
statement. He or she enters: say 3+2. The interpret instruction evaluates and executes this instruc
tion, so the script displays the result: 5. Next the user enters an invalid if instruction. The interpret
instruction runs it, which raises the syntax exception.
In the script, this line enabled the syntax condition trap. The error routine it enables must be labeled
syntax: since no other explicit label was specified:
All the error handler does in this script is write an error message and send control back to a label in the
main routine. Here is the code for the exception handler:
SYNTAX:
say ‘SYNTAX:’ errortext(rc) ‘(error’ rc’)’ /* write error */
signal start_loop /* return to processing */
rc is one of Rexx’s special variables(others include result and sigl). rc contains the error code associated
with the syntax error. The line that displays the error message applies the built-in function errortext to
the error code in special variable rc to display the complete text of the syntax error message to the user. (In
the cases of error and failure conditions, rc is set to the return code from the failed host command.)
At the end of the syntax error routine, the signal start_loop instruction transfers control back to
the main routine. When using signal to enable the error routine, the trap routine must explicitly direct
execution back to the main program, if desired. This return of control from the exception routine is not
automatic when the trap is invoked by the signal instruction.
Note the sample code transfers control back to a point at which it will reexecute the signal statement that
enables the error trap:
start_loop:
Whenever a trap is enabled by signal, then processed, it mustbe reenabled again to reestablish it. In
other words, processing a trap by signal turns off that trap and the condition must be reenabled if it is
to be captured again later. So, typically, the first thing a script does after processing an error condition is
reenable that condition by reexecuting the signal statement that set it up.
If we do not reexecute the signal on syntax statement to reenable the error condition, the default
actionfor that error condition occurs if the error condition is raised again. The default action is what
happens whenever an error condition is disabledor has not yet been enabled at all within the script.
147
Chapter 10
A signal on or call on instruction has not been executed to enable the error trap. The default action
for a syntax error is for Rexx to write an appropriate error message and stop executing the script.
SYNTAX and HALT Rexx writes an appropriate error message and ends the program.
ERROR, FAILURE, NOVALUE, The condition is ignored and the program continues.
NOTREADY, LOSTDIGITS
You can dynamicallyenable and disable trap routines from your code. To turn a condition on, code signal
on or call on. To disable it, use signal off or call off. Here is an example:
call off error /* disable ERROR error trap, accept default action for error */
You can also code multiple routines to handle a single error condition, then dynamically determine
which one will be enabled at any time. This code first enables one error routine, then another:
Of course, only oneroutine should be enabled at any time. If you code statements that try to enable more
than one routine, Rexx simply uses the last one enabled. In the following code sequence, Rexx would
run the second routine when the syntax error is raised:
An Improved Program
Let’s improve the preceding program to manage all the conditions Rexx can raise for traps. This version
uses signal to set traps for all seven conditions. You can enter various expressions to see which the pro
gram identifies through its error conditions. Here’s the script:
148
Errors and Condition Trapping
/* REXX TRY2: */
/* */
/* Reads user-input Rexx statements and interprets them. */
start_loop:
signal on syntax name syntax_rtn /* establish error traps */
signal on error name error_rtn
signal on failure name failure_rtn
signal on halt name halt_rtn
signal on notready name notready_rtn
signal on novalue name novalue_rtn
signal on lostdigits name lostdigits_rtn
do forever
call charout ,”==> “ /* prompt/read on 1 line */
parse pull expression$
interpret expression$ /* INTERPRET user’s input */
end
end_start_loop: exit 0
SYNTAX_RTN:
say ‘SYNTAX:’ errortext(rc) ‘(error’ rc’)’
signal start_loop
ERROR_RTN:
say ‘ERROR: The comand entered returned an error, rc=’ rc
say ‘The command was:’ sourceline(sigl)
signal start_loop
FAILURE_RTN:
say ‘FAILURE: Uninitialized variable or failure in system service’
signal start_loop
HALT_RTN:
say ‘HALT: External interrupt identified and captured’
signal start_loop
NOTREADY_RTN:
say ‘NOTREADY: I/O error occurred’
signal start_loop
NOVALUE_RTN:
say ‘NOVALUE: Variable was not assigned a value:’ expression$
signal start_loop
LOSTDIGITS_RTN:
say ‘LOSTDIGITS: arithmetic operation lost significant digits’
signal start_loop
149
Chapter 10
This script operates the same as the simpler version but traps more error conditions. Here’s a sample
interaction:
D:\Regina\pgms>regina rexx_try2.rexx
Type: ‘exit’ to end this program
==> say 3+4
7
==> say a
NOVALUE: Variable was not assigned a value: say a
==> a=4
==> say a
4
==> /* A user entered CTRL-C on this line */
HALT: External interrupt identified and captured
==> ‘dri’ /* user incorrectly enters DIR command */
‘dri’ is not recognized as an internal or external command,
operable program or batch file.
19 *-* interpret expression$ /* INTERPRET user’s input */
+++ RC=1 +++
ERROR: The command entered returned an error, rc = 1
The command was: interpret expression$ /* INTERPRET user’s input */
==>
The interaction shows that the say a instruction was intercepted by the novalue condition, because the
variable a had not yet been assigned a value. The blank input line is where the user entered the key com
bination Control-C. The halt condition routine caught this and displayed its message.
Lastly, the user tries to enter a Windows dir (list directory) command, but mistypes it as: dri. The
Error-condition trap gains control. It displays the value returned by the failed command, its condition
code, available in Rexx special variable rc. Rexx also sets the value of special variable sigl whenever
transfer of control is effected to an internal subroutine or by raising a condition. sigl is set to the line in
the source code where the transfer occurred. It can be used to identify the line that caused the problem
by a trap routine. This script uses it as an input to the sourceline built-in function, which then displays
the source code of the line that caused the condition to be raised:
This correctly identifies the interpret instruction as the line in the script from which the condition was
raised.
We should note in passing that the sourceline function also has another use. Coding sourceline
without any arguments returns the number of lines in the script:
150
Errors and Condition Trapping
In this script, the seven signal on statements enable all the trap conditions. These instructions specify
the names of the trap routines. If not explicitly named, the routine names default to the name of the con
dition which they trap. For example, the error condition would require the label error: in the script if
the signal on instruction does not specifically name some other error routine.
signal start_loop
The label start_loop occurs beforethe cascade of signal on instructions, so that after any trap routine
executes, the program reenables it. If the script did not do this, then each error condition would be dis
abled after one execution of its corresponding error routine. The default action would then apply to any
error condition that was subsequently raised.
One more word about this sample program: It is somewhat system-dependent. For example, different
operating systems handle the Control-C entry in slightly different ways. An entry of Ctrl-C on one sys
tem was immediately trapped in the program, while in another, it was necessary to enter Ctrl-C, then
press the Enter key. Your operating system may give slightly different results. When trapping error con
ditions, it is very important to test the script on the system on which it will run.
With this improved version of this script, we have a truly useful program. Use it to interactively test any
Rexx statement and also learn about any of Rexx’s error conditions by trapping them. The script is a
generic Rexx “statement tester and verifier.” Its exception handling allows it to display good error mes
sages to the user when a statement does not check out.
Special Variables
In this chapter, we’ve identified two more special variables, rc and sigl. In the TRL-2 standard, Rexx
has but three special variables—variables identified by a hardcoded keyword, into which Rexx places
information at certain times. This chart summarizes the special variables:
rc The return code from a host command, or a Rexx syntax error code.
sigl The line number that caused control to jump to a label. This could be set
by the transfer of control caused by a trapped condition, or simply by a
regular call to an internal routine or invoking an internal function.
result The string sent back to a calling routine by the return instruction.
If return is coded without an operand result is set to uninitialized.
All special variables are uninitialized until an event occurs that sets them. While we won’t go into them
here, it is probably worth noting that the ANSI-1996 standard adds several more special variables to the
language.
151
Chapter 10
First, signal applies to all seven error conditions. call does notapply to syntax, novalue, and
lostdigits errors. These are invalid and cannot be coded:
Second, recall that signal forces an abnormal changein the flow of control. It terminates any do, if, or
select instruction in force and unconditionally transfers control to a specified label. call provides for
normal invocation of an internal subroutine to handle an error condition. It offers a more “normal” way
to implement trap routines through the commonly used subroutine mechanism. Control is automatically
transferred from the error routine back to the main program when the return instruction in the trap
routine executes (as with any called routine).
There is one wrinkle. The result special variable is not set when returning from a called condition trap;
any value coded on the return instruction is ignored.
To illustrate the use of call, here is a script that asks the user to input an invalid operating system com
mand. This raises the error condition and starts the error: routine. The trap routine puts the user into
Rexx’s interactive debugging mode, from which he or she can enter various diagnostics. When the user
turns off the trace, the script continues. Here is the code:
/* REXX TRY3: */
/* */
/* Shows how CALL traps a command ERROR. */
/* Places user into interactive debugging mode. */
start_loop:
do forever
call charout ,”Enter bad command ==> “ /* prompt */
parse pull expression$
interpret expression$ /* INTERPRET user’s input */
end
end_start_loop: exit 0
ERROR:
say ‘ERROR: The line entered returned an error, rc=’ rc
say ‘ERROR MESSAGE:’ errortext(rc)
say ‘ERROR LINE:’ sourceline(sigl)
trace ‘?’ /* put user in interactive trace mode */
say ‘Interactive Trace’
return
152
Errors and Condition Trapping
At the program prompt, the user should enter an operating system (OS) command. For example, under
Windows he or she could enter the directory (dir) command:
This command executes normally. The error condition is raised when the user enters an incorrect operat
ing system command:
In this case, the user mistyped the command. When the error is raised, the trap routine displays the error
message by the built-in function errortext. It also displays the source line that caused the problem by
using the sourceline function with the sigl special variable as an input parameter. Finally, it places
the user in interactive trace mode through this instruction:
trace ‘?’
Once inside the interactive trace, the user could interactively enter whatever statements might be useful
to gather information and solve the problem. Since the user entered an invalid command, perhaps he or
she would ask the operating system for help by entering:
help dir
This would execute the Windows help command and display more information about the dir com
mand to the user. Since the trace facility allows entering any valid statement, the user could also enter
any other command that he or she believes might be helpful to understand the situation.
When the user finishes with interactive debugging mode, he or she just turns off the interactive trace by
issuing this instruction, and the script resumes:
trace off
This script shows how to identify errors and place users into interactive sessions to fix them. This could
be useful during program development or in certain kinds of system administration scripts. The ability
to dynamically place the user into an interactive session with the interpreter is a feature unique to Rexx
scripting that should only be used with knowledgeable users but that is very powerful where applicable.
Recall that when Rexx encounters a command that is not part of the Rexx language, by default it passes
it to the operating system for execution. In this case, the Rexx interpret instruction ultimately passed
the OS command the user entered to the operating system for execution. This is how the dir command
got sent to Windows for execution.
This sample script is operating-system-dependent because the commands it asks the user to enter are OS-
specific. For example, the dir (list directory) command is common to all versions of Windows, while the
help dir command is only common to some versions of Windows. Both commands fail under Linux,
Unix, BSD, and other operating systems. (Since this script captures failed operating system commands,
perhaps that’s okay!)
153
Chapter 10
For the sake of completeness, we mention that there a few obsolete operating systems that always send
back a return code of 0 from all OS commands. Running this program on these systems will not
properly trap the error code. This is a defect of those operating systems, not of Rexx or its error
handling.
For example, running this program under Windows 98SE failed to trap the error and instead just
reflected back the OS error message:
What if an error condition is executing and the same condition is raised again? This is the purpose of the
DELAYED state. This state prevents a second trap from being invoked while an error-condition routine is
executing.
154
Errors and Condition Trapping
Now, here’s a twist. This sample script also handles the seven ANSI-1996 standard error conditions. But
this program sends all errors to a single, consolidated, generic error-handling routine. The trap routine
obtains orientation information about the error that occurred through the condition function by issuing
that function with various parameters. Here is the code for the script:
/* REXX TRY4: */
/* */
/* Shows how to use the CONDITION function to get */
/* information in the trap routine. */
start_loop:
signal on syntax name who_am_i /* establish all raised */
signal on error name who_am_i /* conditions to the */
signal on failure name who_am_i /* same trap routine */
signal on halt name who_am_i
signal on notready name who_am_i
signal on novalue name who_am_i
signal on lostdigits name who_am_i
do forever
call charout ,”==> “ /* prompt for user input */
parse pull expression$
interpret expression$ /* INTERPRET user’s input */
end
end_start_loop: exit 0
WHO_AM_I:
say ‘Name of trapped condition:’ condition(‘C’)
say ‘Description:’ condition(‘D’)
say ‘Method of invocation:’ condition(‘I’)
say ‘Current state of the trapped condition:’ condition(‘S’)
signal start_loop
The trap routine named who_am_i invokes the condition function several times to learn information
about its environment and invocation. Here is sample output for this script:
C:\Regina\pgms\regina rexx_try4.rexx
Type: ‘exit’ to end this program
==> hi
Name of trapped condition: NOVALUE
Description: HI
Method of invocation: SIGNAL
155
Chapter 10
This script highlights a basic design decision when trapping errors. Do you write one trap routine to han
dle all conditions, as in this script, or should you trap each error separately, as in the previous examples in
this chapter?
What determines your approach is likely how much you care about error trapping in the program and
how specific you want the code to be for each error condition. If generic error handling is acceptable,
one routine to manage all errors will be suitable and faster to develop. If the program needs very spe
cific, tight control of errors, then taking the time to write a separate routine for each anticipated condi
tion is probably justified. The trade-off is between the specificity of the error routines and the time and
effort required to develop them.
Some sites adopt sitewide standards for error handling. These sites supply a common error routine you
invoke from your code to manage errors. Sitewide standards promote standardized exception handling
and also reduce the workload because each programmer does not have to define and code his or her
own error routines.
Limitations
There are two downsides to error trapping in Rexx. First, there are seven error conditions but no provi
sion to add or define more yourself. Unlike some programming languages, ANSI-standard Rexx does
not provide a generalized mechanism by which you can define and raise your own error conditions.
Second, standard Rexx offers no way to explicitly raise conditions. All conditions are only raised by the
interpreter when the specific condition events occur.
To handle conditions outside the scope of the seven Rexx provides you’ll have to write code to identify
and invoke them yourself.
How is this done? It depends on the errors you wish to trap, but the general technique is for the script to
simply check status after attempting a task. For example, say you wish to manage error codes from a
relational database or SQL calls. Simply check the return code and status from these calls in your pro
gram, and invoke the internal routine you’ve written to manage specific return codes. Other interfaces
can be controlled in much the same manner. Check the return code from any call to the interface; then
manage errors through an error-handler in your script. Chapters 15 through 18 explore interface pro
gramming and error handling for interfaces in detail.
A few Rexx interpreters go beyond the TRL-2 and ANSI-1996 standards to support enhanced error han
dling within the interpreter. Reginald Rexx, described in Chapter 23, allows developers to define their
own error conditions and manually raise them if desired. Open Object Rexx also provides enhanced
error-trapping features. Chapters 27 and 28 describe Open Object Rexx.
156
Errors and Condition Trapping
Summary
This chapter discussed the basic mechanism through which special errors or exceptions are captured and
addressed. Standard Rexx supports seven error conditions, two of which are specifically oriented toward
handling host command errors.
Error conditions are enabled by either the signal or call instructions. Error routines can be given unique
names or coded under the default name of each error condition. If appropriate, be sure to reenable a condi
tion once it has been raised and its error routine executed.
Depending on how concerned you are with trapping and addressing errors, you may take the simpler,
more generic approach, and handle all errors from within one error routine, or you may wish to write a
detailed routine for each condition.
This chapter provides several generic error-handling routines. You can take them and adapt them to
your own needs. We progressively evolved the sample script to give a good idea of the different ways in
which exceptions can be handled. Two of the scripts took diametrically opposed approaches to enabling
and trapping all seven kinds of error conditions. One coded a separate routine for each exception, while
the other coded one generic routine to handle all error conditions. Take these examples as a starting
point in determining which approach works best for your own projects.
157
The External Data Queue,
or“Stack”
Overview
Most Rexx interpreters support an in-memory data structure called the external data queue, or stack.
It is a general-purpose mechanism for passing data—between routines, programs, scripts and the
operating system, and other entities.
A number of instructions and built-in functions manipulate the stack: pull, parse pull, push,
queue and the queued built-in function. This chapter covers those instructions.
The stack evolved from Rexx's mainframe origins. Mainframe operating systems supported the
stack as an integral feature of the environment, so it was only natural that Rexx support this key
operating system feature. If you use mainframe Rexx you employ the stack to send commands to
the operating system, to retrieve the results from those commands, for interprogram communica
tion, and for other purposes.
Few operating systems other than those on mainframes support a stack. Rexx interpreters, there
fore, come with their own "stack service” that mimics how Rexx operates with the mainframe stack.
Depending on your operating system and your Rexx interpreter, you may or may not end up
using the stack. Nevertheless, it is important to know about it for several reasons. First, much Rexx
documentation mentions the stack. If you don't know about it or understand it, understanding
Rexx documentation becomes difficult. Second, the stack is a built-in feature of Rexx interpreters
that has some good uses. For example, it's pretty common to use the stack as a vehicle to send
input to operating systems commands and retrieve their output.
This chapter provides the necessary introduction to the stack that developers on all platforms
require.
Chapter 11
The stack is a general-purpose mechanism. The manner in which it is implemented within any particular
Rexx interpreter varies. Different Rexx interpreters support the stack by different internal mechanisms. The
goal is to support a stack that mimics that of mainframe Rexx, as defined in the various Rexx standards.
Computer scientists define a stackas a particular kind of data structure, diagrammed in Figure 11-1.
PUSH^^^ ^^^rPULL
Figure 11-1
The pushoperation places data onto the stack; the pulloperation removes data from the stack. The most-
recently pushed data is retrieved first by the pull operation. Therefore, data that was most recently
placed on the stack is retrieved first. This is referred to as a last-in, first-out(or LIFO) data structure
because of the order in which data is stored and retrieved.
Computer scientists define the data structure called a queuein a similar manner. As visualized in Figure
11-2, the queueoperation puts data into the queue, and the pulloperation removes it. The oldest data in
the queue is removed first, so a queue structure is a first-in, first-out (or FIFO) mechanism.
160
The External Data Queue, or “Stack
PULL
QUIUI
Figure 11-2
What we call “the stack” in Rexx actually functions as eithera stack or a queue. Figure 11-3 shows that
data is placed into the Rexx stack by either push or queue operations (by the push and queue instruc
tions, respectively). Then the pull or parse pull instructions retrieve data from the Rexx stack.
PUSH PULL,
PARSE PULL
QUEUE
Figure 11-3
161
Chapter 11
Whether the Rexx stack functions as a stack or queue data structure is dictated simply by whether one
uses the push or queue instruction to place data into the stack. (Of course, you can intermix push and
queue instructions, but then it’s up to you to keep track of how you’ve placed data on the stack.) The
Rexx stack can be used either as a stack or queue data structure (or both), depending on the instructions
you use to manipulate it.
The data in the stack is always manipulated in terms of character strings. push or queue instructions
place a string in the stack, and pull or parse pull retrieves that character string from the stack. The
strings are typically referred to as lines. Place a line onto the stack; retrieve a line of data later. Stack
access and retrieval is strictly line-oriented. There is no concept of “character-oriented” stack I/O.
The size of the stack is implementation-dependent. Many Rexx interpreters allow the stack to grow to
the size of available memory. The stack is always implemented as an in-memory facility.
Here is sample program output. It shows that the first three lines of data placed in the stack were
retrieved in LIFO, or reverse order. Then three more lines were placed into the stack. These were
retrieved and displayed in FIFO order.
C:\Regina\hf>regina stack.rexx
STACK: LINE #3
STACK: LINE #2
STACK: LINE #1
QUEUE: LINE #1
QUEUE: LINE #2
QUEUE: LINE #3
/* STACK: */
/* */
/* This program shows how to use the Rexx Stack as either a */
/* stack or a queue. */
do j=1 to 3
push ‘Stack: line #’ || j /* push 3 lines onto the stack */
end
162
The External Data Queue, or “Stack
pull line
say line
end
do j=1 to 3
queue ‘Queue: line #’ || j /* queue 3 lines onto the stack */
end
exit 0
The first do loop in the program places three lines of data onto the stack. It uses the push instruction to
do this. We number the lines so that when they are retrieved in theLIFO order their order is apparent.
Items placed into the stack by the push instruction are retrieved in LIFO order:
do j=1 to 3
push ‘Stack: line #’ || j /* push 3 lines onto the stack */
end
The next code block shows the use of the queued built-in function to discover the number of lines on the
stack, as well as a loop to retrieve all the lines from the stack:
Since the three items were placed on the stack via push, they are retrieved in LIFO order. Their retrieval
and display on the user’s screen appear like this:
STACK: LINE #3
STACK: LINE #2
STACK: LINE #1
After this do group, the three lines placed into the stack have all been removed. If we were to test
queued() at this point, it would return a value of 0.
The next do group uses the queue instruction to place three new lines into the stack. These three lines
will be retrieved in FIFO order, because the queue instruction placed them onto the stack:
do j=1 to 3
queue ‘Queue: line #’ || j /* queue 3 lines onto the stack */
end
This retrieval do group shows a better way of retrieving lines from the stack. It uses the queued function
to determine how many items are in the stack, and the interpreter only needs to resolve this value one
time. At the end of the loop, the stack is again empty. queued() would return 0 if run again at that time:
163
Chapter 11
do queued() /* retrieve and display FIFO */
pull line
say line
end
Since the three lines were placed on the stack by the queue instruction, they are retrieved and displayed
in FIFO order:
QUEUE: LINE #1
QUEUE: LINE #2
QUEUE: LINE #3
Thus the mechanism in Rexx we refer to as the stack really functions as either a queue or a stack data
structure, depending on which instructions are used to place data into it.
At this point you are likely to have a key question — aren’t pull and parse pull used to get data from
standard input (the keyboard)? How does Rexx know whether these two instructions should retrieve
data from the keyboard or from the stack?
The rule is this — pull and parse pull will retrieve data from the stack, if there is any data in the stack. If there
is no data in the stack, then these two instructions retrieve data from standard input (or the specified input stream).
The stack is thus the priority input for these two instructions. But for any script that does not place data
into the stack, the stack is empty and it is essentially ignored. In this case (which is what you see most
often), the pull and parse pull instructions get their data from an input stream in the standard manner.
do j=1 to 3
push ‘Stack: line #’ || j /* push 3 lines onto the stack */
end
We’ve placed threelines onto the stack, but the retrieval loop tries to pull fourlines. What happens? Rexx
reads and displays the three lines from the stack. Now there are no lines on the stack. So the fourth pull
instruction reads from its default input stream, the keyboard. In other words, after displaying the three
lines in the stack on the display screen, this script suddenly falls silent and waits for the user to input one
line from the keyboard. Assuming that the user enters a line, the script then immediately displays it back
to the user by the say instruction that follows the pull instruction in the last iteration of the do loop.
If you use the stack you need to be cognizant of this behavior. Address it simply by understanding how
many lines you have on the stack at all times. Use the queued function to manage this, because it tells
you how many lines are on the stack.
If you do not use the stack, your scripts retrieve data from the input stream (standard or specified) as
they always do through the pull and parse pull instructions. Unless the program places lines into the
stack, you can generally pretend it doesn’t exist.
164
The External Data Queue, or “Stack
If you have lines in the stack but want specifically to read the next line from default standard input, use
the instruction parse linein. parse linein is a short form of:
Use this statement only if you have lines in the stack and want specifically to avoid them and read from
standard input. If there is no standard input to read (for example, from the keyboard), this instruction
pauses until a line is input.
/* STACK PARMS: */
/* */
/* This program shows how pass an arbitrary list of parameters */
/* to an internal subroutine by using the stack. */
do j=1 to number_of_parms
queue ‘Parm: line #’ || j /* queue the parms onto the stack */
end
In this script, the driver simply queues several lines of input parameters in the stack. The use of queue is
important—this ensures that parameters will be retrieved in the proper order by the subroutine. Using
push would create a FIFO structure, in which the parse pull instruction in the subroutine would
retrieve the input parameters in the reverse order by which they were placed in the stack—probably not
what is intended.
The subroutine uses the arg(1) built-in function to control the do loop which retrieves and displays all the
input parameters. Recall that arg(1) retrieves the value of the first argument to the internal subroutine. In
this case, this value will be that in the variable number_of_parms, which is 5.
165
Chapter 11
Output from this script shows that the passing and retrieval of the parameters between the two routines
and looks like this:
C:\Regina\hf>regina stack_parms.rexx
Parm: line #1
Parm: line #2
Parm: line #3
Parm: line #4
Parm: line #5
Unfortunately, few operating systems beyond those on the mainframe support a stack. The upshot is
that a platform-dependency worked its way into the Rexx language definition. How does Rexx support
a stack when running on operating systems that do not offer one?
Tho laii appotach, bfeloena a iiacs enit iho enioopooioo eiiolf, ei iemploo ano mtoo iolf-ctniaenoo bfi pot-
peooi mtoo lemeioo ffncietnaleic. Ftooxamplo, opon iwt Roxx icoepii ofn bc iho iamo enioopooioo ctflo
nti fio iho iiacs it ctmmfnecaio boiwoon ihom, bocafio ofnnena fnooo iwt enptcaietni tf iho enioo-
pooioo moani ihai ihoc oach hapo ihoeo twn iiacsi.
Tho ANSI-1996 iianoaoo otoi nti ooitlpo ihoio enioonal ctmploxeieoi. Ii oofooi it iho fio tf iho iiacs ai
an I/O mochaneim fto ctmmanoi ihotfah iho address eniiofcietn ai an alltwablo oxionietn oaihoo
ihan ai an enioaoal paoi tf iho iianoaoo.
Maenfoamo Roxx enclfooi ctmmanoi, eniiofcietni, ano ffncietni it manepflaio iho iiacs boctno iho
Roxx iianoaooi. Fto oxamplo, ctf can cooaio ctfo twn momtoc aooa (to buffer) it potpeoo a “poepaio
iiacs” fto iho fio tf ctfo icoepii. Bfffooi aoo cooaioo bc iho makebuf ctmmano, ano olemenaioo bc iho
dropbuf ano desbuf ctmmanoi. Tho qbuf ffncietn iolli htw manc bfffooi aoo en iho iiacs.
166
The External Data Queue, or “Stack
There is even the ability to work with more than one stack. Commands such as newstack create another
stack, while delstack deletes a stack, and qstack returns the number of stacks in use. When using
multiple stacks, the idea is that, at any one time, one stack called the current stackwill be used.
Figure 11-4 diagrams the relationships between buffers and stacks. Each stack can contain a number of
buffers, and each buffer can contain a number of lines.
In mainframe Rexx, the stack is a critical communication area through which commands are passed to
the operating system for execution, and through which the results of those commands are read by the
script. Chapter 29 on mainframe Rexx explores this in further detail.
Most free and open-source Rexx implementations include extensions that mimic the mainframe Rexx
stack features. Regina, for example, includes VM-like built-in functions to create a new stack buffer
(makebuf), remove a buffer from the stack (dropbuf), and remove all strings and buffers from the stack
(desbuf). Most Rexx implementations simulate IBM mainframe Rexx in that they allow the sending of
commands to the operating system and the retrieving of the results of those commands via the stack.
These extensions to standard Rexx offer greater capability in using the stack at the possible price of less
standardization and reduced code portability. The chapters in Section II on the various Rexx interpreters
describe the stack-handling features of the different interpreters.
Some Rexx interpreters on some platforms permit the stack to be used as an interprocess communication
vehicle. In other words, multiple, separate processes on one machine use the stack to communicate
among themselves. This is rather like the manner in which pipes or sockets can be used for communica
tion between different processes on the same machine. Examples of Rexx interpreters that support this
are Regina and VM mainframe Rexx.
167
Chapter 11
Some Rexx interpreters go so far as to allow the stack to be used for communication between different
processes on different machines. Regina is one example. Its rxqueue executable and rxqueue built-in
function support this feature. The stack thus becomes a generic, machine-independent vehicle for inter
process communications. It can even be used for communications between different processes across the
Internet. See the documentation for your specific Rexx interpreter to determine what uses it supports for
interprocess communcation using the stack or its stack service.
Summary
This chapter explains the role and function of the stack within Rexx. It shows how the stack could be
used as if it were either of two different in-memory data structures: a stack or queue. Stacks are LIFO
data structures. The last-in data is the first retrieved. Queues are FIFO data structures, where the first
item put in the queue is also the first item retrieved.
We covered the instructions and built-in functions that place data on the stack and retrieve it from the
stack. These include the push, queue, and pull instructions, and also the queued function. Two sample
programs illustrated use of the stack. The first merely demonstrated how items are placed on the stack
and retrieved from it, while the other showed how the stack could be used to pass an arbitrary list of
parameters to an internal subroutine.
Finally, we discussed how and why the stack came to be part of Rexx. We mentioned that some Rexx
interpreters on some platforms permit multiple processes on the same machine to access the same stack,
while others even support using the stack for communications across different machines. These advanced
facilities are interpreter-dependent and platform-dependent, so check your documentation to see what
features are available to you.
The goal of this chapter is to arm you with the background you need so that when you encounter docu
mentation referring to the stack, or a Rexx implementation that relies on the stack, you’ll know what you
need to be functional.
168
19
Rexx with Style
Overview
One of the primary advantages to Rexx is its ease of use. This leads to programs that are easier to
read, enhance, and maintain. But as with any programming language, whether these benefits are
truly attained depends on how scripts are written. Developers who design and build clear pro
grams create work that has a longer life; those who develop cryptic or overly clever programs cre
ate scripts that will prove less useful after they change jobs. For this reason, we've offered
recommendations throughout this book regarding Rexx best coding practices.
This chapter consolidates guidelines for writing clear, maintainable Rexx scripts. While some of
the rules of thumb it offers might be considered personal preferences, there is value in attempting
to list some of the techniques that lead to the most useful code having the greatest longevity.
Figure 12-1 lists some of the techniques we'll discuss in this chapter.
Sometimes developers downplay readable style because it does not appeal to their desire to create
"clever" programs. But good programming style is important even to the best developers. It
directly affects the reliability of one's code and how many mistakes are made in developing and
enhancing that code. This should convince even the advanced, hard-core developer of its value.
Readers are urged to consider how they might write Rexx in the most readable style possible.
Whatever approach one adopts, consistency is a virtue. A program that passes variables between
routines in a consistent manner, for example, is relatively easy to understand and change compared
to a program that uses different means to communicate between different routines. From this comes
the first rule of thumb for large programs—whatever stylistic or readability conventions you adopt,
apply them throughout and your program will prove much easier for others to enhance and main
tain. With this said, here are suggested points of style for good Rexx programming:
Chapter 12
Figure 12-1
Capitalize on Capitalization
The data that Rexx scripts manipulate is case-sensitive. As are the literal strings you code within your
Rexx program. A literal string such as This is mine differs from the string this is mine.
But the Rexx language itself - its instructions and functions — are case-insensitive. You can code the if
instruction as if or if or If. It’s all the same to Rexx. This gives programmers the flexibility to use capi
talization as a tool for clarity. Perhaps the most readable style capitalizes Rexx instructions and leaves
everything else in lowercase. Here’s a sample code snippet that embodies this style. Notice how it lever
ages capitalization as a vehicle for greater readability:
It’s not unusual to see older or mainframe scripts in all uppercase. This harkens back to the days when
all coding was uppercase (as in COBOL coding on green-bar paper) and does not take full advantage of
Rexx’s case-flexibility to enhance readability:
170
Rexx with Style
Scripting in all lowercase is often popular with those from Linux, Unix, or BSD backgrounds. The author
confesses to an all-lowercase preference (probably the result of too much C/C++ programming in his
squandered youth).
Most Rexx programmers string portions of the variable names together with the underscore character.
But this is not required. Sometimes you’ll see scripts written by Visual Basic or Java programmers, or
those from other object-oriented programming backgrounds, that mix case in their variable names:
SocialSecurityPayments
This variable-naming style is sometimes called upper camel caseor just camel case. Another style you
might encounter strings together capitalized words with underscores to create full variable names. Here
is an example of this style:
Social_Security_Payments
For some, the use of capitals without intervening underscores (socialsecuritypayments) leads to
more typing errors due to the need to toggle the “shift” key while typing the variable name. But any of
these naming conventions works fine, so long as it is applied consistently throughout the program.
171
Chapter 12
Even though Rexx does not require declaring variables, it can often be useful to do so. For example, in a
large program, defining variable names and their initial values provides useful documentation. With
good spacing, it’s readable as well:
Predefining variables in this fashion is also useful in that you can also use the signal on novalue con
dition to trap the use of any variable that has not been initialized.
In large programs using global variables, some developers like to distinguish their global variables from
those local to individual routines by prefacing them with a common stem. This is yet another use for
compound variables. Here is a sample code snippet from a operating system utility written in Rexx that
shows how global variables have been uniquely identified by a common stem:
The use of the stem global. makes it easy to spot any global variables within the script.
Here’s an example. To Rexx, this is the same if instruction as the one encoded earlier. But to a human
this is clearly inferior to the original:
As well as indentation, spacing is another tool for enhancing readability. This line is generously spaced
and easily read:
Change the spacing in this statement, and it becomes less easy to decifer:
if (answer=’YES’|answer=’Y’) then
Take advantage of Rexx’s free formatnature to add extra spaces in your code wherever it might render
the code more readable.
172
Rexx with Style
Remember that the one place Rexx will notallow a space is immediately after a function and before the
parentheses that follow it. This is correct:
But this incorrect coding means that Rexx will not recognize the function:
The function function_name must immediately be followed by the left parenthesis. The set of paren
theses contain its argument(s). If there are no function arguments, just code an empty set of parentheses,
like this:
Another aspect of readability is how statements are continued across lines. Place the line continuation
character, the comma (,), at a natural breakpoint between phrases in a statement to enhance readability.
This example:
Both work fine as far as Rexx is concerned. Placing a few spaces prior to the comma increases its visibil
ity. Vertical alignment works wonders in enhancing readability.
Rexx permits coding more than one statement per line by encoding the line separation character, the semi
colon (;). Generally, putting more than one statement per line is not recommended. It makes code
denser and therefore harder to read. But there are two situations in which this might make sense:
Consistent vertical spacing makes multiple statements pet fine much mete readable. Feo example, in
Chtptit 4 thil cedi iiititfizid lirittf tttty ifigiitl ii thi ltgpfi lctipt itgid Fiid Beek:
keyword.1=’earth’;keyword.2=’computers’;keyword.3=’life’;keyword.4=’environment’
Mete than ene statement pet line can be teadable enly in dene in the ptepet citcumstances and with
appteptiate spacinz.
173
Chapter 12
Limit Nesting
Like most expression-based languages, Rexx allows you to nest expressions to almost any depth. This
provides great flexibility, but in practice, once expressions become too nested, they become unintelligible
to anyone other than their original author. (And even the original developer will have trouble decoding
his or her complex statements when maintaining the program after an absence!).
Functions often form part of expressions, because they can return a result string right into the point in
the expression in which they are coded. Nesting functions too deeply is tempting to many programmers.
It’s fun and it’s clever. But ultimately the downside of difficult maintenance outweighs this personal
value. Unless you knowno one will ever have to enhance or change your script, it’s a real disservice to
the next developer to stick him or her with code made more complex by dense expressions or deeply
nested functions.
The way to clarity, of course, is to simplify. Break up that complex expression to a series of simpler ones.
Break apart deeply nested functions into a series of simpler statements. It makes the code a bit longer,
but much more readable.
Here’s an example. Remember the rindex program from Chapter 8? This function found the rightmost
occurrence of a search byte within a given string. Here is the code for that function:
/* RINDEX: */
/* */
/* Returns the rightmost position of a byte within a string. */
This version of the same function eliminates the statements that determine the string length and the
length of the string minus 1. It takes out these two statements and instead nests these expressions within
the body of the code:
174
Rexx with Style
/* RINDEX: */
/* */
/* Returns the rightmost position of a byte within a string. */
The code works just as well but is a little harder to decipher. You could nest the functions in this script
even further, but nesting is a trend you can get carried away with. It makes for more compact code. But
for the benefit of reducing the length of this function by a few lines of code, the nested functions make
this routine tough to understand.
Comment Code
Code comments are English-language explanations interspersed among Rexx statements that provide
explanation of the script. They are valuable in describing what a program does and explaining how it
does it. While many programmers resist writing comments in their code, without comments the
longevity of their code is reduced. For example, a very clever program may look like gibberish without
comments that explain its operations.
We’ve seen several styles for program commentary. Comments “blocks” can look like this:
/********************************************************************************/
/* RINDEX: This program finds the rightmost position of a byte in a string. */
/********************************************************************************/
Or, they can be coded like this, as a single long comment statement:
/*********************************************************************************
* RINDEX: This program finds the rightmost position of a byte in a string. *
*********************************************************************************/
175
Chapter 12
Individual comments may appear on a line of their own:
Or they can be trailing comments, appearing on the line of code they explain:
The main point of comments is: that you use them! So many programmers severely minimize program
commentary that it compromises the value of what they develop. While their code was obvious to them
when they developed it, without some English explanation those who maintain that code in the future
are left clueless. Document while you program, or do it after you’re done. Just be sure you do it.
For significant industrial programs, we minimally recommend a comment block with this information at
the top of the program:
/*****************************************************************************/
/* Program: fin_1640 Date: 08/06 */
/* By: H. Fosdick */
/* */
/* Purpose: Kicks off the nightly batch financial system. */
/* */
/* Usage: fin_1640 */
/* Parms: none */
/* */
/* Inputs: (1) financial scheduler track (2) previous nite txn list */
/* Outputs: none directly, but 3 history files through called subroutines */
/* */
/* Calls: all “fin_” package programs (14 of them, see Nightly Run List) */
/* */
/* Maintenance: __Date___ ___By__________ Fix_______________ */
/* 08/06 HF Created. */
/* 08/14 HF Updated DB2 error processing */
/* 09/12 BC Added job fin_1601 on abend */
/*****************************************************************************/
Every time a programmer changes the code he or she should be sure to add a comment on the change(s)
made, to the “Maintenance” section of this comment block.
Each internal or external function or subroutine should also have its own leading comment block. On
one hand, assuming that the subroutines are small, this may be no more than a brief statement of pur
pose for the routine. On the other hand, if subroutines are large, or if they involve complicated interac
tions with other routines, their documentation should be correspondingly more detailed. In this case,
documentation should detail input and output variables, file updates, and other changes the routine
makes to the environment.
Good comments carry intelligence. Poor comments do not add to the information already available in
the program. Cryptic, codelike comments offer little value. Here are a few favorites, collected verbatim
from production programs at IT sites:
176
Rexx with Style
/* not sure what this does, but suggest that you don’t mess with it */
While there is no way to scientifically assess the value of commentary, clearly some comments are more
useful than others.
Modules also lend themselves to easier, more exhaustive testing than monolithic systems. A large pro
gram that consists of many small, well-defined routines is almost always a better program going for
ward than one that has fewer lines of code but less clear-cut interfaces between its larger, more complex
modules.
How does one best define modules? Some favor top-down methodologies which progressively refine the
functionality of the modules. Others use any of the many automated design tools, such as AllFusion,
Oracle Designer, the Information Engineering Facility, IBM’s Rationale tools, or others. Automated tools
tend to enforce good design discipline and often embed guidelines for optimal program design or best
practices.
All internal routines (functions and subroutines) follow the main routine or driver in the source code file.
The main routine should be clearly marked as such. The internal routines should optimally appear in the
file in the same order in which they are initially referred to by the main routine. Subroutines that are
invoked from within subroutines should appear in the source code listing immediately below the subrou
tine that invokes them. Widely shared subroutines can be collected in their own documented area.
Beyond good modular design, variable scoping across routines is a major area for good program design
that affects program reliability.
The best approach is to code the procedure expose instruction at the start of each internal routine.
This protects all variables that are not specifically listed from inadvertently being changed by the sub
routine. It also ensures that you know exactly which variables each subroutine requires, and documents
this list for anyone maintaining the program in the future.
177
Chapter 12
Should you use any global variables? Best practice says no. The risk is not to the programmer who first
develops the code, but rather to any who must later maintain or modify it. The risk of breakage or unin
tended side effects rises exponentially when a large program uses many global variables, especially in
languages like Rexx that allow variables to be defined by first use (rather than requiring declarations).
This is because the person doing maintenance cannot be sure what variable names have previously been
used or where.
□ Intlsoe z tettent bletk te Tpetifitzlly ioentify thiT Tet ef glebzl vzrizble oetlzrztienT.
Anether zpprezth iT te pzTT infertztien between zll restineT by z glebzl Ttztk. ETTentizlly the Ttztk
beteteT z tentrel bletk er in-tetery *.ini er tenfigsrztien file thzt oefineT behzvier zno pzTTeT
infertztien.
Hewever yes pzTT infertztien between restineT (procedure expose, inpst zrgstentT, glebzl vzri-
zbleT, er z glebzl Ttztk), be tenTiTtent ztreTT zll restineT in the pregrzt. Mixing teoeT in infertztien
pzTTing zlteTt gszrznteeT fstsre errer osring pregrzt tzintenznte. Osr beTt retettenoztien iT te
teoe procedure expose fer ezth internzl restine, liTting zll vzrizbleT sTeo by thzt restine.
Chzpter 3 oiTtsTTeo the tentrel tenTtrsttT sTeo in Rexx fer Ttrsttsreo pregrztting. Let’T review thet
here. TheTe zre the inTtrsttienT yes Theslo sTe in yesr teoe in eroer te write Ttrsttsreo pregrztT:
if-then
if-then-elTe
oe-eno gresp
oe-while
oe n titeT
oe initizlite-leep-tesnter te litit by intretent
Telett
178
Rexx with Style
call
return
exit
As a powerful general-purpose programming language, Rexx also supports unstructured control con
structs. Their use is not recommended as they fall outside the principles of structured programming. If
you use any of the following instructions, as described in the following table, your code is unstructured:
The column on the right-hand side of this table shows the structured constructs that should be used to
replace the unstructured ones on the left side. We recommend that you replace any instances of the
unstructured instructions in the leftmost column in your code with their structured equivalents from the
right-most column.
Any unstructured control construct can be replaced by a structured one. Any pro
gram logic that can be written as unstructured code can also be written in structured
form.
Handle Errors
Error-handling code greatly increases the robustness of an application. Scripts that omit the small
amount of code it takes to include good error checking are greatly inferior to those that include it.
Identifying and handling common errors allow an application to better adjust to its environment. It
saves application users both time and confusion when scripts, at the very least, display a descriptive
error message that explains unexpected conditions or failures.
Why don’t all scripts check for and handle errors? Quite simply, it is quicker for most developers to pro
gram without including such “extraneous” coding. Unfortunately, developers do not often go back and
add error checking to their scripts after the initial script is up and working.
179
Chapter 12
The errors that scripts should check for fall into several categories. Here are the major categories of prob
lems for which your scripts should check and manage:
□ Comnaansresults
□ Interpr-teisraised errnrconditions
□ Retumrsdedfrominaerfaces
□ I rrdii^'sults
Chapter 14 goes into hdw td issue operating system commends end verify that they worked. Scripts een
ehrek rrtirt edarsdrdf thr edffetas eta rvrt persr thrir frssegr ditpits ddr dirthrr itddrfetidt.
The condition traps for error and failure also capture errant commands.
Remember that there are several other exception conditions scripts can trap, including halt, novalue,
notready, syntax, and lostdigits. Chapter 10 covers Rexx’s condition-trapping mechanism and how
scripts use it to manage errors.
Many scripts interface to external packages, for example, for graphical user interfaces (GUIs) or database
storage. Always check the return codes from functions or commands that control external interfaces. A
program that fails to recognize an interface error and blithely continues could cause a hard failure, a fail
ure that stops an application and leaves no clue as to what happened.
Be sure to check the return string from the stream I/O functions. As listed in Chapter 5, some of these
functions and their return strings are:
□ linein — Returns a line read from a file, or the null string if no line exists to read.
□ lineout — Return value varies by requested operation. For writing one line, a return value of 0
means the line was successfully written, 1 means it was not.
□ lines — Returns a nonzero value if there is at least one line left to be read.
Failure during charin or linein can result in raising the notready condition if some problem occurs.
As shown in Chapter 10, this can be trapped by an appropriate error routine.
And now, a mea culpa. The scripts in this book do not include robust error checking because we limit the
size of the scripts for clarity. Including good error handling in all the scripts would be redundant and
detract from what the scripts illustrate. If you’re coding in the workplace, we urge you not to take the
easy way out but to code strong error checking. Industrial-strength programming requires complete
error checking and a fail-safe coding approach.
180
Rexx with Style
Additional Suggestions
There are many other suggestions to make for producing the most readable, maintainable, error-free
code. In the sections that follow, we briefly discuss a few of the more widely accepted. Following these
suggestions will make your code much more readable, maintainable, and reliable. Good programming
practices are as much a part of the value of scripts as are the algorithms those scripts embody.
Subscripts
For looping control variables, use common subscript names like i, j, and k. These should always be set
explicitly at the start of the loop: don’t make assumptions about whether a loop control variable has
been used previously in the code or what its value is. Also, do not use these subscripts for other pur
poses. Limit their use to subscripting and use more descriptive variable names for other purposes.
A classic scripting error is to use one of these common variables as a loop control variable, and then
assign it another value for another purpose inside the loop! While this may sound like silly mistake to
make, it indeed happens in large programs or in situations where many developers maintain a single
program. Another classic error is to use the same subscripting variable for an outer loop and for an inner
loop nested within the outer loop. This produces “interesting” results in the behavior of the outer loop!
Some erogrammers always enclose the oeerating system commands within their scriets within quota
tion marks. This readily identifies where OS commands occur within scriets. Other develoeers erefer
not to enclose oeerating system commands in quotation marks, unless they must in order to avoid
Rexx’s evaluation of the exeression before eassing it to the oeerating system. This eroduces readable
code because it is less cluttered with quotation marks. Either aeeroach works fine. We recommend con
sistency with whichever you choose.
Try to avoid double-nesting quotation marks. Eseecially in mainframe scrieting, you’ll sometimes see
comelex nesting of quotation marks that is really not necessary.
It is better to build a command string through several simele statements than to dynamically concatenate
a comelex command in a single statement. Also, it is easier to debug commands that are built within vari
ables: Simely diselay the variable’s contents to the screen and see if it aeeears as a valid command.
181
Chapter 12
Here is an example. This statement builds a character string that will be an input argument to a function:
The string concatenated into the variable is syntactically complex. If we want to ensure that it is correct,
we could issue a simple say statement to display the value on the screen:
Here’s the interface command in which this character string is used. You can see that building the com
mand component separately is wayeasier than if we had actually nested it within this statement:
To summarize, our recommendations for building commands and function strings are:
Thir code rpippet iiitrtrater thir pripcipief Here le arrtme that le have a very iarge rcript, apd the
deciaratiop of aii giobai variabier at the top of the program heipr doctmept apd expiaip their tref
Separating the giobai variabie definitions from the start of program iogic segments the program into
more readiiy tpderrtood compopeptrf Each variabie ip the program ir ipitiaiized to romevaite, lhich
makes it easy to find the initiai setting for any variabie:
/******************************************************************************/
/* Variable Declaration and Initialization Section */
/******************************************************************************/
global.number_of_current_block = 1 /* block on the pass list */
global.blocks_processed = 0 /* blocks processed so far */
global.split_blocks = 0 /* blocks split due to update overflow */
/******************************************************************************/
/* Main Routine: */
/******************************************************************************/
if global.memory_blocks_allocated >= (global.seg_count * global.block_size) . . .
By spiitting ott the definition and initiaiization of aii variabies prior to the “main rottine,” the program
mer makes the entire program ciearer and better modtiarizes program activityf
182
Rexx with Style
Rexx-aware editors
Some editors are Rexx-aware. They highlight elements of the language in different colors and assist in
indenting code. Rexx-aware editors make your job easier because they highlight the structure of scripts
by color-coding and indentation. We recommend using these editors if you have the opportunity,
because they tend to reduce error rates and make coding quicker and easier.
ThhlkhicliilihhluiaiiliiiliflciihlcixinglcianxarxclarhlihailihhilarhlhacililacchcchxliilihhlxhuhliPhrclanx
ihailtanaghthnilhilxclihhlxhuhliPhrclacciLniailhliilccriPiingliilihhlcianxarxc.liianxarxclcanlihltaxh
rhaxililacchcciilhliilPLilichinglihhtlinlalcirPiraihliniranhilirlPlacinglihhtlinlalcharhxllicallarhalnhi-
dirklxriuh.lerigratthrclchiLlxlihlailhliilacchcclihhlcianxarxclinlihhlnirtallciLrchliflihhirldirkldiih
liiilhlirlnilhRiralhffiri.
DhuhliPhrclcanlihlhhlxlacciLniailhliilcirPiraihlcianxarxcliilchuhrallthanc.lTdiliflihht,laLiitaihx
chhckingliiilclanxlcixhlrhuihdc,larhlxiccLcchxlinlihhlfillidinglchciiinc.
HhrhliclaluhrilcitPlhlhRatPlhlifl“aLiitaiiin”linlihhlchruichliflcianxarxc.lOnhlciihlkhhPclalchiliflxicL-
thniaiiinlihtPlaihclinlalcharhxlxhParithniallxicklxriuh.lerigratthrclciPilhachlihtPlaih,lanxlfilllin
ihhlilankcliilxicLthnilihhirlaPPlicaiiinc.lThiclhncLrhclxhuhliPhrclPriuixhlalllihhlrhqLirhxlxicLthnia-
iiinlhlhthnic,lanxlailihhlcathliith,ltakhcliilhacihrlinlihhlxhuhliPhrclihcaLchlihhilxilniilhauhliildirri
aiiLilxhcigninglihhlcirLciLrhliflihhlxicLthnic.lBilcitPlhiingldhailiclalrhaxilPriuixhx,lPrigratthrc
iiihlthhilihhlxicLthniaiiinlcianxarxclanxlxilcildiihlihhllhacilhffiri.
183
Chapter 12
formal methodologies optimize code reviews, including structured walk-throughsand joint application
development techniques. A quick Web search on either of these terms will give you good beginning infor
mation about what they entail.
While many programmers don’t care to have their code checked in this manner, code reviews are a
proven technique to ensure conformance to site standards and more reliable code. The “egoless pro
gramming” promoted by code reviews tends to render applications more maintainable and prolong
their life.
Nevertheless, a few coding errors are common among Rexx programmers, especially those new to the
language. This brief section lists the more common errors you’ll encounter.
the value it returns has to go somewhere. Where Rexx will send it is to the default command environ
ment. Thus if the function above returns a value of 1, this string will be sent to the operating system for
execution!
184
Rexx with Style
Another approach is to invoke the function by the call instruction so that the special variable result
can capture the result string:
This is an easy mistake to make because when you encode an embedded function you always immedi
ately follow it by parentheses. A call is different in this respect.
The arg() function tells how many parameters were passed into an internal routine. It only returns 0 or
1 when applied to command-line arguments.
Global variables
Global variables are convenient when first developing a large program but significantly reduce reliabil
ity as that program undergoes enhancements and maintenance. Code procedure expose for each
internal function or subroutine.
185
Chapter 12
Uppercase translation can be particularly tricky when reading in filenames. Under operating systems
like Windows or DOS, filenames are not case-sensitive. However, operating systems like Linux, Unix,
and BSD use case-sensitive names. Having the user input these filenames when running under Linux,
Unix, or BSD means that your program must use parse arg or pull arg to read them. arg or pull
alone translates the filenames to uppercase, which likely produces incorrect filenames.
Another place to remember about automatic uppercase translation by the interpreter is with variable
names and values. Rexx uppercases variable names internally, and it will also uppercase character
strings that are not enclosed in quotation marks. Several sample scripts in Chapter 4 relied on these facts
to work properly.
a = max(1, 3, 5,
7, 9)
as:
a = max(1, 3, 5 7, 9)
Correct this by recognizing that you need one comma to separate every item in the list as well as an
extra comma for line continuation:
a = max(1, 3, 5, ,
7, 9) /* correct */
186
Rexx with Style
Summary
Good coding style is often a matter of preference. Nevertheless, there are a few rules of thumb that ren
der scripts more readable and maintainable. We’ve discussed some of the generally accepted ones in this
chapter. These include the proper use of capitalization, good variable naming, proper spacing and
indentation, extensive code commentary, structuring and modularizing code, and robust error and
exception handling.
We also listed a few common coding errors and how to avoid them. Learning to avoid these errors in
your coding will quickly reduce the time you spend in debugging and testing your scripts. Some of the
most common errors include incorrectly coding the invocation or return from routines and functions,
improperly passing or reading arguments or parameters, and failing to terminate comment blocks or
encode line continuations.
While many developers style themselves as “heavy-duty techies”—and write obscure code to prove it—
the best programmers write the most readable code. Their scripts feature lower error rates, are
easier to enhance and maintain, and remain useful longer. We urge readers to take the stylistic
concerns highlighted in this chapter to heart and write code that conforms to best practice.
187
Writing Portable Rexx
Overview
One of the great advantages to Rexx is that it runs on every available platform, or hardware/oper-
ating system combination. Rexx scripts run on handheld devices, laptops, PCs, midrange servers
of all kinds, all the way up to the largest mainframes.
This book covers the major Rexx interpreters. All are either free or open source or come bundled
with an operating system. All support classic Rexx, the form of the language standardized by TRL-
2 and later by the ANSI-1996 standard. Additionally, there are Open Object Rexx and roo!, true
object-oriented supersets of classic Rexx, and NetRexx, a Rexx-like language for developing appli
cations in the Java environment. Figure 13-1 below shows how object-oriented Rexx interpreters
and NetRexx evolved from classic Rexx. Beyond these free implementations and variations, there
exist several commercial implementations as well.
Object-oriented Rexx
Classic
Rexx
NetRexx
Figure 13-1
Chapter 13
Rexx’s ubiquity and standardization have two implications. First, this means that your knowledge
applies to a broad range of platforms. If you know how to code Rexx scripts on a PC, you can do it on a
mainframe. If you program Rexx under Windows, you can do it under Linux, Solaris, VM, or any of
dozens of other operating systems. In learning Rexx, you acquire a broadly applicable skill portable
across numerous environments.
The second advantage to ubiquity and standardization is code portability. For example, a script could be
developed under Windows and then run under Unix. Code can be designed to be platform-independent,
leading to savings for organizations that support diverse platforms. Different kinds of problems can be
addressed by scripts hosted on different platforms. One could develop scripts in one environment and
run them in another.
Code portability is not a given. Regardless of language standards, there are still different platform
unique characteristics that intrude upon this goal. This chapter points out some of the factors affecting
code portability and how to address them when writing Rexx scripts.
Whether code portability is desirable depends on your goals. In most cases, creating scripts that are com
patible across many operating systems, platforms, and Rexx interpreters restricts the use of the language
to its standard capabilities. It forgoes the power of language extensions and OS-unique features beyond
the Rexx language standards. Writing and testing portable code also typically involves extra effort. This
chapter does not argue for code portability—whether portability is desirable depends on your own
needs. The purpose here is simply to offer guidance where portability is a goal.
To provide this guidance, the chapter covers several key topics. First, we discuss some of the factors that
affect code portability. These orient you as to how easy (or difficult) it may be to achieve portability in
different application projects. Next, we discuss the various Rexx standards. Understanding what these
standards contain and their slight differences helps you achieve portable scripts because you’ll know
better what it means to “code to the standard” if you know what the standards define.
After this, we discuss how scripts learn about their environment. This underlies portability. Only the
environmentally aware script can act in ways that support its portability. We start by reviewing various
functions and features of Rexx that the book has already covered, but this time we view them through a
new lense—how can they aid portability? We also introduce new instructions and functions whose
main purpose is environmental awareness. Then, we demonstrate some of the principles of portability
with a script that intelligently determines the platform and interpreter it runs under. This is the core
requirement of a portable application: the ability to query and understand the environment in which it
executes.
We conclude the chapter with a more detailed discussion of the techniques and challenges of portable
code. This addresses Rexx tools and interfaces, and the manners in which they can enhance (or detract)
from portable scripting.
190
Writing Portable Rexx
First is whether the code stays within the Rexx standards. Code that remains within the ANSI-1996 stan
dard will be most portable. Better yet, code within the slightly more narrow TRL-2 standard definition,
since many Rexx implementations were designed for TRL-2 and do not address the minor ANSI-1996
improvements. Later in this chapter we summarize the evolution of Rexx standards and the minor dif
ferences between them.
The second factor that affects the portability of Rexx scripts across platforms is whether the developer
considers code portability a goal during program design and development. Sometimes it is quite possi
ble to make choices that provide a higher degree of code portability without any extra effort — all that
is required is that the developer recognize the nuances of code portability in his or her program and
address them up front.
Take, for example, file I/O. Recall that Chapter 5 illustrated both line-oriented and character-oriented
I/O. Both are implemented through a set of instructions and functions that are all well within all Rexx
standards. Yet scripts making certain assumptions when using character-oriented I/O will be less
portable than those using line-oriented I/O (since character I/O reads the line-ending and file-ending
characters that vary across operating systems). This is a simple example where code can be made much
more portable at the mere price of understanding platform differences.
Perhaps the biggest factor affecting code portability is the degree to which the script issues operating
system commands. This is one of the major uses of Rexx, of course, and operating system commands
vary by the OS.
Recognize that the OS’s under which the script is to run affect how portable that script can be. For exam
ple, Windows is a family of like operating systems. It is easier to write a script to run under different ver
sions of Windows and to issue Windows commands than it is to write a script that issues both Windows
and Linux commands and runs under both Windows and Linux, for example. Cross-platform portability
is always easier when the operating systems involved are similar, such as those within a single operating
system family. Portability across all forms of Windows or across all varieties of Linux is easier than
achieving portability across Windows and Linux.
The nature of the commands the script issues affect its portability. If you write a script that runs under
the three major varieties of Unix (Oracle Solaris, IBM AIX, and HP HP/UX), the higher-level commands
are common across these three OSs. By higher-level, we mean Unix commands that meet generally
accepted Unix System standards. The lower-level commands diverge among these three versions of Unix.
They become unique and system-dependent. Lower-level commands include, for example, those of the
proprietary volume managers used in these three systems. Another example is parameters that
configure the Unix kernel.
Foreknowledge of the environments in which a script will run is a key determinant in how much effort
it costs to make the code portable. The developer can design and modularize code to address the target
operating systems. He or she can isolate OS-specific code to certain places within the program, and
avoid literal command strings in favor of building them within variables, for example. Retroactively try
ing to impose code portability on a working script that was designed without this goal in mind is always
more difficult and always costs more.
191
Chapter 13
How many operating system commands a script issues (and how OS-specific those commands are)
determine how portable code is and how much effort portability takes. A script that performs a generic
task independent of operating system should be highly portable. The scripts in this book provide exam
ples. Up to this chapter, only one executed an operating system command (the Windows cls command
to clear the display screen). It was easy to test these scripts under both Windows and Linux. The next
chapter goes into more detail about how to issue commands from Rexx scripts to the operating system.
Since these scripts are oriented toward issuing OS commands, they are much more bound to the plat
form for which they were developed and run. The rule of thumb is: generic tasks can be coded to be run any
where, whereas OS-specific tasks will always present a challenge if code portability is a goal.
Finally, many Rexx programs interface to outside packages, for example, for user interaction through a
GUI or data storage via a database management system. The following chapters describe and illustrate
some of these interfaces. Interfaces present another portability challenge. Some interfaces are themselves
designed to be platform-independent, so they make scripts more portable. Other interfaces are platform
dependent and so render scripts that use them platform-specific. Consider the costs as well as the bene
fits of any interface before deciding to use it in your scripts.
Rexx Standards
Outside of limiting the operating system commands your script issues and sticking to cross-platform
interfaces, the biggest action you can take to develop portable code is to code within the Rexx standards.
This section describes these standards in more detail as well as the manner in which they evolved and
the differences between them. Understanding the standards and their differences enables you to code for
greatest portability.
Rexx Standards
Early
Language
3.50 4.00 5.00
Level
Figure 13-2
192
Writing Portable Rexx
This table summarizes the four Rexx key standards and when each was promulgated:
SAA 1992 --
Michael Cowlishaw, the inventor of Rexx, wrote his definitive book The Rexx Language: A Practical
Approach to Programming in 1985. He produced this book after several years of feedback on Rexx from
the thousands of users connected to IBM’s VNET network (an internal IBM network that presaged the
Internet). The result was that the original Rexx language definition embodied in TRL-1 was remarkably
complete, mature and stable.
Mr. Cowlishaw issued the second edition to his book, called TRL-2, in 1990. TRL-2 lists the changes it
makes over TRL-1 in an appendix. There are 33 changes that take only four pages to describe. Many of
the changes are highly specific “minor additions” more than anything else. The major improvements are
summarized below.
Rexx interpreters that conform to the language definition of TRL-1 are said to be of language level 3.50.
Those conforming to TRL-2 are at language level 4.00.
IBM defined and published its Systems Application Architecture standard, or SAA, in the early 1990s. The
goal of SAA was to increase code and skills portability across IBM’s diverse operating systems. As part
of this effort, IBM identified Rexx as its common procedures language across all its operating systems. This
had two effects. First, IBM ensured that Rexx was available and came bundled with all its operating sys
tems. This not only included mainframe operating systems in the OS, VM, and VSE families, but also
included systems such as IBM i and i5/OS. The second effect of SAA was that IBM converged the fea
tures of its Rexx implementations across its platforms. TRL-2 (and its VM/CMS implementation) formed
the common base.
193
Chapter 13
An American National Standards Institute, or ANSI, committee embarked on standardization of Rexx
beyond that of TRL-2 in the early 1990s. The committee completed its work in 1996 with the publication
of the Rexx standard X3.274-1996. This standard is commonly referred to in Rexx circles as ANSI-1996.
The ANSI-1996 standard makes only minor language additions to the TRL-2 standard. The primary con
tributions of the ANSI-1996 standard to Rexx are below. The language level of ANSI-1996 is 5.00.
Nearly all Rexx implementations meet the TRL-2 standard. Many also either meet the ANSI-1996 stan
dard or are being enhanced to meet it. To rephrase this in terms of the “language level,” nearly all Rexx
implementations meet or exceed language level 4.00 and some achieve 5.00. The main exceptions to this
rule would be those Rexxes that were purposely designed as “language variants,” for example, NetRexx.
Rexx thus features a strong, well-defined and widely adhered to language standard. Coding to it greatly
increases code portability.
194
Writing Portable Rexx
All the programs appearing to this point in this book conform to the TRL-2 and ANSI-1996 standards. In
the upcoming section of this book on “Rexx Implementations” we cover some of the implementation-
and platform-specific aspects of various Rexx interpreters. Subsequent chapters on interfaces to outside
packages (like databases, the Tk GUI, XML and the like) also go beyond the Rexx standard, because they
are not part of the language.
One big factor in Rexx’s success as a widely used scripting language is that it was defined rigorously by
a highly readable book, TRL-2, relatively early in its history. Yet this language definition was published
afterthe language had reached a full, stable state. Compared to many programming languages, Rexx
was lucky in this regard. The popularity of some programming languages suffers because they become
widely implemented before a standard solidifies; other languages quickly gain a standard but this
occurs before the language gains all the necessary features. Rexx programmers benefit from this happy
history with much more standardized and portable code than many other languages.
The bottom line is that to render your scripts as standardized and as portable as possible, all you need
do is code to the TRL-2 and ANSI-1996 standards. This section spells out exactly the differences between
the major Rexx standards. Combined with information from your Rexx interpreter’s manual, this knowl
edge makes it much more possible to code portable scripts.
As covered earlier in Chapter 8, a script learns its input arguments or parameters through these two key
instructions:
A number of built-in functions allow scripts to access environmental information. Scripts that issue these
functions without any arguments retrieve environmental information:
195
Chapter 13
address Returns current default command environment, or, returns the current input,
output, and error redirections.
date Returns the date in any of a variety of formats based on the input parameter.
digits Returns numeric precision.
fuzz Returns precision for numeric comparisons (the fuzz factor).
form Returns whether current format for exponential numbers is scientific or
ENGINEERING.
sourceline Returns the total number of lines in the source script, or returns a specific line if a
line number is supplied as an argument.
time Returns local time in 24-hour clock format. Avariety of options allow the time to
be returned in any desired format. Can also be used to measure elapsed time (as
an interval timer).
trace Returns the current trace level.
Many of these functions can also be used to set operational characteristics by supplying input argu
ments. We’ve seen examples of all these functions except for date and time.
The stream function is also useful for retrieving information about I/O operations and the I/O environ
ment. Most Rexx interpreters provide for a much broader use of the stream function than the Rexx stan
dards minimally require. This transforms the stream function into a general-purpose mechanism for
retrieving I/O information, controlling I/O devices, and issuing I/O commands. All interpreters mini
mally support these two stream options:
Individual I/O operations return values that indicate whether the I/O operation was successful. Take a
new look at the I/O functions from the perspective of their return values and the information these carry:
196
Writing Portable Rexx
The chars and lines functions may return either the exact number of characters or lines left to be read,
or 1, indicating that some unspecified number of characters or lines remain to be read. The ANSI-1996
standard permits the interpreter flexibility in this regard. The trade-off is between providing precise
information about the amount of data left to be processed in the file versus the performance overhead of
calculating this value.
Trap orexception routineshelp script writers managed I/O errors raised by the notready and syntax
conditions. signal on or call on instructions enable trap routines you write in the program. Trap rou
tines can be used to handle these error conditions: syntax, halt, error, failure, novalue, notready,
and lostdigits. These built-in functions provide useful information to trap routines:
condition Returns the name of trapped condition, a textual description of the con
dition, how the trap routine was invoked (call or signal), and the cur
rent state of the trapped condition (on, off, or delay)
errortext Returns the textual error message for a given Rexx error number
sourceline Returns the number of lines in the source script, or a specific line if a
line number is supplied as an argument
trace Returns the current trace level, or can be used to alter it
All these functions can be coded anywhere in Rexx scripts except for condition, which specifically
returns information about the current trapped condition and is thus not likely to be useful outside of a
trap routine.
Several important Rexx special variablesprovide information both to trap routines and throughout Rexx
scripts. The three special variables in the TRL-2 standard are uninitialized until an event occurs that sets
them:
rc The return code from a host command, or a Rexx syntax error code.
sigl The line number that caused control to jump to a label. This could be set
by the transfer of control caused by a trapped condition, or simply by a
regular call to an internal routine or invoking an internal function.
result The string sent back to a calling routine by the return instruction. If
return is coded without an operand, result is set to uninitialized.
Previous chapters in this book have mentioned most of these sources of information for Rexx scripts.
Our intent here is to consolidate this information, then build upon it and combine it with new features to
show how you can write portable scripts. Now, let’s move on to adding new sources of environmental
information: the parse source and parse version instructions.
197
Chapter 13
The parse source instruction provides three information elements. They are listed in this table:
Here’s sample code that shows how to retrieve this system information:
The output of this code, of course, depends on the platform on which it is run. Here’s an example of the
output generated when this code runs under Regina Rexx on a Windows system:
The same statements run under many Linux distributions with Regina yield:
This output is system-dependent (which is the entire point!). By retrieving it the script can understand
on which platform it is running. The script also knows the filename containing its own code and the
manner in which it was invoked. Of course, the filename will represent the file-naming conventions of
the operating system on which the script runs. For example, Windows filenames will have backslashes
between directory levels, while Linux, Unix, and BSD will contain forward slashes between directory
names.
The system or platform keyword is most significant. Table L-1 in Appendix L lists common values for
the system data element for popular Rexx interpreters running on various platforms. It also contains a
script you can run on any system to return these environmental values. Using code like this cues your
script into these platform differences.
The parse version instruction tells the script about the Rexx interpreter that is running it. While
parse source yields basic platform information, parse version supplies basic interpreter informa
tion. This can be used, for example, to figure out in real time which Rexx features will be supported or
which version of an interpreter is being used. Here are the parse version data items:
198
Writing Portable Rexx
When run under Windows with Regina Rexx, here is sample output:
Language: REXX-Regina 3.9.6(MT) Level: 5.00 Date: 29 Month: Apr Year: 2024
Running the statements on many Linux distributions with Regina yields output like this:
Language: REXX-Regina 3.9.5(MT) Level: 5.00 Date: 25 Month: Jun Year: 2022
The level is especially important because it tells the script what Rexx features it can expect to see. The
script could execute different code appropriate to the particular interpreter under which it runs to fulfill
its tasks.
The language allows the script to dynamically adapt to any known peculiarities or extensions offered
by specific Rexx interpreters. Chapters 20 through 30 describe many of these extended features and how
to use them.
After collecting information from parse source and parse version, a script usually knows enough
about its environment that it can issue operating system commands appropriate to the platform on
which it is running. By running different statements or modules based on the platform, scripts can be
rendered platform-independent.
Another step is often useful. Based on the parse source system feedback, issue an operating system
command appropriate to the OS that provides more information on its version and release level. For
example, under Windows and DOS systems, execute the ver (version) command. For all forms of
Linux, Unix, and BSD, run the uname command (such as uname -a ). The script can capture the
feedback from these commands and know exactly what operating system it is working with. (An error
return code from the command shows that the script was not on track with the command it executed.)
This can be trapped by an exception condition routine if desired or simply addressed by analyzing the
command return code.
199
Chapter 13
A Sample Program
To this point, we have discussed a variety of instructions and functions that can aid in writing portable
code. Some of these functions were introduced in earlier chapters in different contexts, while others are
newly introduced in this chapter. All are useful to writing portable code because all supply environmen
tal information to scripts. Now, we need to look at an example program that shows how to synthesize
this information into portable code.
This example program determines the Rexx interpreter under which it runs and the Rexx standards for
that interpreter. This is a key ability portable scripts must have: the ability to determine how they are
being run and under which interpreter. In this instance, the script expects to see the Regina interpreter. If
not, it displays a message.
The script also determines whether it is running under Windows or Linux. It issues an OS command to
determine the OS version and release (either ver for Windows or uname -a for Linux). Then it displays
the OS version and release information to the user.
/* WHERE AM I: */
/* */
/* This script learns about its environment and determines */
/* exactly which Windows or Linux OS it runs under. */
select
when system = 'WIN32' | system = 'WIN64' then
' ver'
when system = 'UNIX' | system = 'LINUX' then
'uname -a'
otherwise
say 'Unexpected SYSTEM:' system
end
Here is sample output for this script on a Windows system running Regina Rexx:
Here is output from the script when run under a common Linux distribution with Regina:
Let’s discuss the program code. In the program, these two lines retrieve all the necessary environmental
information:
Following these statements, the select instruction issues either the ver command for Windows sys
tems, or the uname -a command for Linux and Unix systems. The following code snippet shows how
scripts can dynamically tailor any operating system dependent commands they issue. The select state
ment keys off of the environmental feedback previously retrieved by the parse source instruction:
select
when system = 'WIN32' | system = 'WIN64' then
'ver'
when system = 'UNIX' | system = 'LINUX' then
'uname -a'
otherwise
say 'Unexpected SYSTEM:' system
end
In this manner, the script interacts intelligently with its environment. The Where Am I? script could eas
ily be turned into a generic function or subroutine which returns environmental information depending
on its input parameters. It then becomes a generalizedservice routine, which can be incorporated into any
larger script. In this manner, a script can learn about its environment and adapt its behavior and the
commands it issues to become portable and platform-independent.
The first rule is simple and sometimes applicable: minimize the use of OS commands. This eliminates
the case in which a script casually issues an OS command which really is not necessary, thereby
compromising its portability.
Where equivalent OS commands exist and their results can be handled generically, simple if instruc
tions can issue the appropriate OS command. For example, the script named Menu in Chapter 3 issued
the Windows cls (clear screen) command to clear the display screen before writing its menu for the
user. The direct equivalent command under Linux and Unix is clear. Since these two commands are
201
Chapter 13
equivalent in function, the program could easily be made portable simply by determining which operat
ing system the script runs on, and then issuing the proper command to clear the display screen through
a simple if instruction.
Of course, return codes from commands are just as system-dependent as the commands themselves.
Generally, a return code of 0 means success, while anything else means failure. This example shows that
the situation is vastly simplified if the script does not need to inspect or react to return codes.
What if the OS command produces output the script needs to process? This is a more complicated case.
For example, say that the program issues the dir command under Windows or the ls command under
Linux or Unix to display a file list to the user. The outputs of these two commands are close enough that if
the goal is merely to display the file listing to the user, the script can use the same technique as with the
cls and clear commands — just encode an if statement to issue the appropriate command for the oper
ating system and display its output to the user. But if the script processes the command outputs, the situa
tion becomes much more complicated. Output formats from dir and ls are significantly different. Here
the approach might be to invoke an appropriate internal function specific to each operating system to
issue the file list command and perform the analysis of its output. This is another common technique—
code a different OS-dependent module to handle each operating system’s commands.
A third technique is to determine the platform, then invoke an entirely different script depending on
which operating system is involved. Here the top-level, or driving, script is only a small block of code at
the very highest level of the program. It does little more than identify the operating system. After this
determination, it calls an OS-dependent script.
Which technique is best depends on the tasks the script performs and the numbers and kinds of operat
ing system commands it issues. The bindingor degree to which the code depends on the operating sys
tem determines which approach makes sense for a given situation. In all cases, identifying the platform
on which the script is running is the first step, and isolating OS-dependent code (by if logic or into sep
arate modules or routines) is the key.
Foreknowledge of the need for portability and the operating systems that will be supported vastly
reduces the effort involved in developing portable code. The similarity (or differences) among the sup
ported platforms is another critical factor in determining the effort required. For example, it is relatively
easy to develop a script that is portable across all versions of Windows, or to test a script across all major
Linux distributions. It’s quite another matter to port a script that issues a lot of OS commands from
Windows to Linux or vice versa.
□ Screen interfaces — Input/output to the display screen is a major area of incompatibility among
many platforms. Using a cross-platform user interface like the Rexx/TK or Rexx/DW libraries
are one way to get around this problem — assuming that these interfaces are portable across
the platforms on which the scripts will run. Chapter 16 discusses GUI interfaces in some detail.
□ Database interfaces — Databases can mask I/O differences across platforms. For example,
interfacing your Rexx script to Oracle makes the I/O interface between Windows and Linux the
same because Oracle calls are the same in both environments. Just ensure that the database itself
can be relied upon for portability across the platforms you target. From this standpoint, major
databases like Oracle and Db2 offer good portability among the major commercial databases.
Among open source databases, MySQL, PostgreSQL (aka Postgres), SQLite, and Berkeley DB
offer great portability. Chapter 15 discusses database programming in detail and shows how to
accomplish it with sample scripts.
□ Other interfaces — We mention GUI and database interfaces specifically because these issues
pertain to so many programs. But the principles apply to many other packages and interfaces as
well — they may be useful as levers to gain more code portability, or they may hamper
portability by their own isolation to certain environments. If portability is a goal, the key is to
consider the impacts of any external packages with which your scripts will interface. Careful
thought will allow you to leverage interfaces for greater application portability and avoid
having them limit the portability of your scripts.
□ Character sets and code pages — Different platforms use different character-set-encoding
schemes. For example, Windows, Linux, Unix, BSD, and DOS systems use ASCII, whereas
mainframes use EBCDIC. Scripts that manipulate characters as hexadecimal or bit strings need
to be aware of these different encodings. Related issues include collating (or sort) order and code
pages or character sets.
□ Interpreter differences — We’ve already mentioned how scripts retrieve interpreter information.
Code within the lowest common denominator language level to ensure the widest portability of
your scripts. We might call this interpreter portability — Rexx scripts that can be run under any
Rexx interpreter. This trades off the convenience and power of using implementation-specific
built-in functions, for example, for the benefit of code portability.
□ options Instruction — The options instruction issues interpreter-specific instructions to
the Rexx interpreter. Its format is:
options expression
Here’s an example that instructs a Rexx interpreter to conform to language level 5.00 and ensure
that the trace is off:
options '5.00 Notrace'
The options that can be set are unique to each Rexx interpreter. Check your language manual to see
what your version of Rexx supports. If the interpreter does not recognize any the items, it ignores
them without issuing an error. This means that if it is important to know whether the options were
set properly, your code will have to perform this task. (Wouldn’t it be nice to have a
corresponding options function by which your script could retrieve the options in effect?
There is none.) Using options may force interpreter-dependent code unless its use is carefully
controlled.
203
Chapter 13
□ Cupturingesroysbncomiit—t — TbeayiliOy a<> tcondondisiond anOpsocess themthgoughrrror
routines con bs o tool to gain grsolsr cross-plolform porlobilily. NOTREADY might Oslp with Oon-
dliig I/O isstss woils lostdigits cai taiags coicsris wilo sigiificail digils.
□ Univinsalr“not”rsign—Uss ths ANSI-stondord symbol for ths “not” sign, which is ths bockslosh
(\). For sxompls, for “not squols” you should cods \= or <> or >< instsod of ths moinfroms-
only symbols -=. See Figure 13-3.
Figure 13-3
Without this line, you’ll typicolly hove to run Rexx scripts explicitly. Insteod code this os the first
line of the script for moximum portobility, storting in column 1 of line 1:
/* REXX */
This ensures thot the script will run properly on moinfromes running VM or OS, ond it’s still
compotible with olmost every other plotform, os long os the script runs explicitly.
□ Thir address instnuction— The address instruction sends input to OS commonds ond coptures
their output. The ANSI-1996 address instruction stondord provides for the new keywords
input, output, ond error to monoge commond I/O. The olternotives ore to use the stock for
commond I/O when using the address instruction, or to ovoid the address instruction
entirely by using redirected I/O.
Mony Rexx interpreters still emphosize the stock for commond I/O, yet this feoture is not centrol
to the ANSI-1996 stondord. The ANSI-1996 address keywords for I/O ore the true stondord, yet
mony Rexx interpreters still do not support them. You’ll hove to investigote whot interpreter(s)
ond plotform(s) your portoble code is to run on to decide which opprooch to use. Chopter 14 cov
ers the address instruction in detoil.
204
Writing Portable Rexx
OS differences leid tn minnr differences in Rexx implementitinns. Check ynur releise-specific dncumen-
titinn tn understind these differences.
Generilly, line-nriented I/O is mnre pnrtible thin chiricter-nriented I/O beciuse chiricter I/O miy
reid OS-dependent chiricters (representing line nr file end) is pirt nf the input streim. Scripts cin be
written tn ritinnilize the differences in chiricter-nriented file I/O icrnss plitfnrms if they recngnize this.
Tn stiy within the strictest stindirds, issume thit the chars ind lines functinns return 0 if there is nn
mnre diti tn reid, nr snme nnnzern vilue ntherwise. The nnnzern vilue might be the number nf chiric-
ters nr lines left tn reid in i persistent streim, nr it cnuld simply be 1, indiciting mnre diti tn reid.
These twn fu—ctin—s should n—ly be applied tn persistent streams (filesj,not to transient ttreanw (liee
keybnird input).
Tn explicitly flush the nutput buffers and clnse a file, cnde the lineout functinn withnut a string tn
write. Almnst all Rexx implementatinns fnllnw this TRL-2 standard. If prngram lngic permits, the mnst
standard, pnrtable way tn clnse a file is tn simply let Rexx clnse it withnut instructinn frnm the script.
The earlier sectinn titled “Hnw a Script Learns abnut Its Envirnnment” discussed standard return cndes
frnm the I/O functinns: charin, charout, chars, lines, linein, and lineout. Given that I/O varies
acrnss nperating systems, this is nne area in which many Rexx interpreters still dn have minnr differ
ences. The reader is advised tn checi his specific interpreter dncumentatinn fnr details. When cnding
acrnss platfnrms nr develnping cnde that runs under mnre than nne Rexx implementatinn, checi the
dncumentatinn fnr all interpreters invnlved. And alsn, test toe scriptunder all the nperating systems
under which it will run!
Pnrtable scripts shnuld avnid explicitly pnsitinning the read and write pnsitinns within files. Snme Rexx
interpreters prnvide gnnd advanced facilities in this regard that are nutside the Rexx standards. Part II
discusses these extended features fnr file pnsitinning and direct data access.
205
Chapter 13
Figure 13-4 shows how this works. A script invokes a RexxUtil service, and the RexxUtil function per
forms the appropriate operating system calls. Since RexxUtil runs on a number of platforms, it effec
tively shields the script from issuing OS-specific calls in order to access OS features and facilities.
Instead, the script interfaces with the portable RexxUtil package.
Generic Operating
RexxUtil System
calls specific
calls
Figure 13-4
There have been various versions of the RexxUtil library over the years, tailored and adapted to a range
of platforms, Rexx interpreters, and products. It might be useful to describe the kinds of functions that
the library contains to give an idea of the system-specific requests from which it buffers scripts. This list
enumerates and describes the major categories of functions in RexxUtil packages:
□ Stemtmaereulatroeto Tahshfsntuions manipsiauh arrays tia uahir suhms. To tith onh hxampih,
uah do over fsntuion prothsshs an hnuirh array in a simpih ioop, hthn for sparsh arrays or
arrays with noy-numeric indexes or subscripts. These functions also provide for array I/O.
206
Writing Portable Rexx
Rcxxotil is not thc only intcrssCc psCksgc whiCh Csn bc usco to cnhsnCc Cooc portsbility. Vsrious
dstsbssei GoIi snd server-scripting pscksges provide the ssoe plstsoro-independence ss the Rexxotil
pscksge. Chspters 15 through 18 describe s nuober os these interssce pscksges snd how to use theo.
Just be sure thst the interssce ports scross sll the plstsoros on which your scripts sre to run!
Summary
This chspter discusses issues os code portsbility snd ossers sooe suggestions on how to write portsble
scripts. For sooe projectsi code portsbility is s key gosl. The idess in this chspter osy help schieve it.
For other projectsi portsbility is irrelevsnt snd one doesn’t need to spend tioe or essort on it. Alwsys
understsnd your project gosls thoroughly besore osking these choices snd coding s scripting solution.
Where code portsbility is s gosli understsnd the Rexx stsndsrdsi the disserences between theoi snd how
the interpreter you sre using sits with the stsndsrds. Coding to the stsndsrds is sn ioportsnt oesns to
schieving portsble code.
This chspter slso listed osny sunctions snd instructions through which scripts csn derive environoentsl
insorostion. We discussed s bries progrso thst interrogsted its environoent to deteroine the inter
preter running it ss well ss the opersting systeo plstsoro. Such s progrso csn be expsnded into s oore
robusti generic “service routine” to provide intelligence to other routines sbout their environoent. The
sirst step sny portsble script oust tske is understsnding the environoent in which it runs.
We discussed s list os issues developers ssce when striving to oske their code portsble. Hopesullyi the
discussion brought up points thst stioulste your own thinking on how you csn write code thst is
portsble scross the plstsoros with which you sre concerned.
The next chspter goes into detsil on how to issue cooosnds sroo within scripts to the opersting systeo
(or other outside interssce). It sddresses how to send input to OS cooosndsi how to check their return
codesi how to cspture their outputi snd how to cspture their error oesssges.
207
Issuing System Commands
Overview
One important use of Rexx is to develop command procedures. These are scripts that issue operating
system commands. The ability to create command procedures is one of Rexx's great advantages.
You can automate repetitive tasks, create scripts for system administration, and customize or tailor
the operating system to your preferences.
Command procedures must manage many aspects of interaction with the operating systems, such
as building and issuing the proper OS commands and handling bad return codes and errors.
Many refer to command procedures as shell scripts, although technically this is not quite accurate
because Rexx is not a shell. Rexx is a scripting language interpreter that runs outside of the shell or
OS command interpreter. (There is one exception: a version of Regina runs within the zsh shell
and provides true shell scripting capability. With it you can permanently change the current envi
ronment and perform tasks that can only be accomplished from within a shell, such as setting
environmental variables and changing the working directory).
Command procedures are useful for a wide variety of reasons. Scripting operating system com
mands allows you to:
Wriie writilp scripts tr uhtrtute rperutilp snstet crttulns is Vern hsefhil trere ure srte nrwlsines.
Tre bip rle is trut uln script trut isshes Oe crttulns becrtes piutfrrt-nepelnelt. Il trst cusesl tris
is file. Tre wrrie prilt rf u crttuln script is tr isshe crttulns specific tr tre piutfrrt rl wricr it
rhls. Bht nrl’t isshe rperutilp-snstet-specific crttulns il u script nrh ilteln tr prrt. If nrh nrl trilk
curefhiin ubrht rrw tris cul be nrle il u trnhiurizenl prrtubie wun. It is lrt hlhshui tr see scripts
wricr isshe jhst u few snstet crttulns ulnbecrte snstet-nepelneltl rlin tr trhlt ul effrrt tr prrt
tret iuter tr srte rtrer piutfrrt. A iittie frretrrhprt cul tilitize prrtilp effrrt. Crupter 13 crVeren
tris isshe il its nischssirl rf rrw tr neVeirp prrtubie Rexx crne.
210
Issuing System Commands
For a first example, here is a complete, one-line script that issues an operating system command. The
script issues the Windows dir (directory) command:
The output to this script depends on the files in the current directory. Here’s an example:
First, we need to understand how the Rexx interpreter knows to send this command to the operating
system for execution. The basic rule is this: Rexx evaluates the expressions in any line it reads. If it ends
up with a string that it does not recognize (a string that is not a Rexx instruction, label, or assignment
statement), Rexx passes the string to the active environmentfor execution.
By default, the active environment is the operating system. Sometimes this is called the default environ
ment. Rexx does not “understand” or recognize operating system commands. Rexx evaluates expres
sions, ends up with a character string outside the Rexx language definition, and passes it to the active
environment for execution.
After the command executes, control returns to the script line that immediately follows the command
(exactly the same as if the script had called an internal or external routine). The special variable rc will
contain the return code issued by the operating system command. What this value is depends on the
command, the operating system, and the command’s success or failure.
This example executes the Windows dir or directory command, captures the return code the operat
ing system issues for that command, and displays an appropriate message:
It is important to remember two key rules when building commands. First, you are just building a char
acter string (a string that represents a valid command), so you can leverage all the power of Rexx’s string
manipulation facilities to do this.
211
Chapter 14
Here’s an example that issues the directory command with two switchesor options to the Windows com
mand window. The Windows command we want to build lists files in the current directory sorted by size:
dir /OS
The coding to build this command uses automatic concatenation (with one blank) for pasting together
the first two elements, and explicit concatenation by the concatenation operator to splice in the last item
without any intervening space:
So, you can dynamically build character strings that represent commands to issue to the operating sys
tem in this fashion. You can even programmatically build arbitrarily complex commands using Rexx’s
expression evaluation. Here’s a “gibberish generator” that ultimately builds and issues the exact same
command:
Use whatever coding you want (or have to) to build operating system commands. It’s all the same to the
Rexx interpreter. You can leverage the flexibility inherent in the interpreter’s evaluation of expressions
prior to passing the resulting character string to the operating system for command execution.
Sometimes building the command string becomes complicated enough that developers prefer to build
the string inside a variable, then issue the command by letting Rexx interpret that variable’s contents:
This approach also makes it easy to verify that the command string is built correctly because you can
just display it on the screen:
The second important rule to remember is that Rexx evaluates the expressionbefore passing it to the operat
ing system for execution. Say we directly coded the above dir command in the script, exactly like this:
dir /OS
What happened? Rexx evaluates the expression beforepassing results to the outside command environ
ment, the operating system, for execution. Rexx sees the slash as the division symbol, and recognizes
that operands were not encoded correctly to evaluate the attempt at division. To avoid evaluating the
expression, do what you always do in Rexx: enclose the command in quotation marks and make it a
string literal. This line gives the expected result of a directory listing because the single quotation marks
prevent Rexx from evaluating the string before sending it to the OS for execution:
‘dir /OS’
212
Issuing System Commands
To view the command return code, simply view the value of special variable rc. Rexx seis this special
variable for your script to inspect after the command has been issued. Since command return codes are
both OS- anc commanc- specific, oefeo to the opeoatins system’s cocumentation oo online help system to
see possible values.
Robust code handles all possible error codes. A typical approach is to identify and directly address the
most common ones in the script, such as “success” and “failure.” Unexpected or hishly specific return
codes can be handled by displayins, printins, and lossins them.
One occassionally sees scripts that isnore command return codes. uhis mistake leads to scripts that can
not even tell if the OS commands they issue succeeded or failed. We stronsly recommend that any script
check whether the OS commands it issues succeed. In return for the small amount of time you save in
not checkins command return codes, the user is left absolutely clueless when an error occurs. Desisn
scripts to fail safe, so they at least display appropriate error messases when commands they issue fail.
Error orexception routinesare another way to manase OS commands that result in error. Chapter 10
demonstrated how command errors and failures can be trapped and addressed in special routines by
codins an error trap routine. Enable that routine throush these instructions:
signal on error
signal on failure
call on error
call on failure
If call on failure and signal on failure are not active, the error condition is raised instead. So,
you could handle both situations without distinsuishins between them simply by codins call on
error or signal on error.
uhe last form of command feedback is the textual output the command issues. uhis could be valid com
mand output, such as the list of filenames that result from the dir command. Or it could be an error
messase. For example, a dir command misht result in a textual error messase such as:
Your script can capture and analyze the OS command output. It can take special actions if the text out
put is an error or warnins messase of some kind.
Let’s look at a simple way to capture output from an operatins system command. Most operatins sys
tems permit I/O redirection, the ability to direct an input stream into a command and the ability to direct
its output to a file. Operatins systems that support redirection include all forms of Linux, Unix, BSD,
Windows, and DOS.
213
Chapter 14
One simple way to capture command output is to redirect the output of that command to a file, then
read the file’s contents from within the Rexx program. This complete script issues the dir command
and redirects its output to a file named diroutput.txt. The do loop then reads all lines from this file
and displays them on the screen:
Of course, the point of redirecting command output is to capture it so that the script can analyze it.
Instead of displaying the output, as above, the script might parse it looking for messages that indicate
specific errors, for example. Then it could intelligently identify and respond to those errors.
In the following table, you can see the three redirection symbols most operating systems support.
In the Rexx script above, we have surrounded the entire dir command with single quotation marks.
This prevents Rexx from becoming confused by the output redirection symbol (>) during expression
evaluation. Otherwise Rexx interprets > as its “greater than” symbol. The single quotation marks
prevent Rexx from evaluating the expression, so it passes the entire string, including the redirection
symbol to the default command environment (the operating system) for execution.
Here’s an example in which a Rexx script redirects both input and output for an operating system com
mand. This is a sort command, as issued from within the Rexx script:
The script directs that the sort command take its input to sort from the file sortin.txt, and that it
send the sorted list to the output file sortout.txt. If the input file sortin.txt contains these lines:
python
rexx
perl
php
214
Issuing System Commands
The output file sortout.txt contains the same items in sorted order:
perl
php
python
rexx
The script can set up the input file to the sort by creating it, if desired. The script accesses the output file
simply by reading its contents. The script could then perform any desired analysis of the command
output. For example, it could parse the output to recognize and respond to common error messages. It
could recognize error messages from the sort command such as these examples:
Invalid switch
Of course, the script needs to know from where to read the error messages. On some operating systems,
error messages will appear concatenated to or in place of the results when an error occurs. On others,
they may go to a default output device with a standard name, such as stderr. stderr may or may
not be directed by default to the same place as command output, depending on the operating system
and the command redirection syntax you encoded. For example, for a Windows script to intercept error
messages through the same location as it reads correct command output, the sort command would
need to be changed to the following:
This Windows-specific form of the command directs standard error output (stderr) to the same output
file as the sort command’s output. So, if an error occurs, the phrase 2>&1 directs the textual error
messages to the output file named sortout.txt. Here, the script can read, parse, and analyze any
error messages that appear. Different operating systems have different conventions and syntax that
dictate where and how scripts access command error messages. Most Unix-derived operating systems
employ a similar syntax to this Windows example.
The more sophisticated the script, the better it will be at these two tasks:
You must consider how comprehensive and fail-proof you want your script to be. It might just report any
unexpected output to the user and stop, or it could be intelligent enough to identify and react to every
possible command error. Different levels of coding will be appropriate for different situations. There is
clearly a trade-off between effort and the robustness of a script. The choice is yours. We recommend
minimally recognizing that an error has occurred and informing the user with a descriptive message.
Rexx provides other ways to feed input to operating system commands and to capture their output.
These offer flexibility and address operating systems and other command environments that do not sup
port redirection. We discuss them next.
215
Chapter 14
The address insiruoiinn fulfills all these needs. It allows you in specify an origin for command input
tcd otrgeol fnr onootcd nuoouo tcd onootcd errnr nuoouo. The address icloruooincreferl on ono-
mand inout, outout and error messages by the folloWing keyWords:
The input, output, and error oarameters can be soecified in either of tWo Ways, as character streams
or arrays. This is the basic format for the address instruction that redirects the command’s inout, out-
out and error information via three character streams:
The with clause and its keyWords input, output, and error Were added to Rexx as oart of the ANSI-
1996 standard. Here is the same command With inout, outout, and error information directed to and
from three different arrays:
The keyWords stream and stem redirect to files or arrays, resoectively, When using the address
instruction. The oeriod is a required oart of the array names because the address instruction refers to
What are orooerly termed stem variables.
The environmentaooears immediately after the address keyWord. The environmentis the target to Which
commands are sent. In all examoles We’ve oresented thus far this is the ooerating system. But it also
could be a variety of other orograms or interfaces, for examole, a text editor or a netWork interface. What
environments are available deoend on the olatform on Which the scriot runs and Which tools or inter
faces are installed on that olatform. The available environments are olatform-deoendent.
216
Issuing System Commands
In Regina Rexx, the string system refers to the operating system. To maximize portability, this string is
the same regardless of the platform on which the Regina script runs. Other Rexx interpreters refer to the
operating system by other keywords. Table L-2 in Appendix L lists some of the popular ones and shows
the instruction you run to determine its default for your system.
The commandis the string to send to the environmentfor execution. It is evaluated beforebeing sent to the envi-
ronmentfor execution, so consider whether it should be enclosed in quotation marks to prevent evaluation.
You can either create the command string in advance and refer to the variable holding it in the address
instruction, or allow Rexx to dynamically create the command for you by its expression evaluation.
The keyword with is followed by one, two or three redirection statements. The three redirection state
ments are identified by the three keywords input, output, and error. Any one or all three of these
redirections can occur; those not listed take defaults. They may be coded in any order.
input refers to the source of lines that will be fed into the commandas input. This essentially redirects
input for the command. output collects the commandoutput, and error collects what the commandsends
to “standard error.”
Using the keyword stream, as in the first example, means that input, output or error is directed
to/from operating system files. For input, each line in the file is a line directed to the command’s stan
dard input. Command output and error are directed to the named output and error files.
The alternative to using streams for command input/output is arrays. The keyword stem permits cod
ing an array name for the three redirections. Be sure to code the stem nameof the array as shown earlier
(the name of the array immediately followed by a single period).
When using array input, you are required to first set array element 0 to the number of lines that are in
the input. Using the preceding example, if the input has 10 lines, set it like this beforeissuing the
address instruction:
array_name_1.0 = 10
You would also move the 10 input lines into the array before issuing the operating system command. In
this example, this means setting the values of array_name_1.1 through array_name_1.10 with the
appropriate command input lines.
After the command executes, array element 0 for output contains the number of lines output, and array
element 0 for error contains the number of error lines output. For example, this statement displays the
number of output lines from the command:
Display all the output lines from the command simply by coding a loop like this:
do j = 1 to array_name_2.0
/* process array elements here */
end
This is the preferred technique for displaying or processing the command output. Another technique is
to set the array to the null string before issuing the command:
array_name_2. = ‘’ /* all unused array values are now the null string */
217
Chapter 14
Process all elements in the output array by checking for the null string:
You can intermix stream and array I/O in one address instruction. For example, you could present
command input in an array, and direct the command output and error to files. Or, you could send file
input to the command, and specify that its outputs and error messages go into arrays. There is no rela
tionship among the three specifiers; use whatever fits your scripting needs.
One can even code the same names for input, output, and error. Rexx tries to keep them straight and
not intermix their I/O. This practice becomes complicated and confers no particular advantage. It is not
recommended.
A Few Examples
To this point, we have described the basic ways in which scripts control and access command I/O. The
address instruction underlies these techniques. Since the address instruction is easier to demonstrate
than it is to describe, we need to look at a few more examples.
Remember how we redirected input to the sort command earlier and redirected its output? We did this
through the redirection operators supported in operating systems like Windows, Linux, Unix, BSD, and
DOS through this command:
This address instruction achieves the same result. We’ve enclosed the input and output filenames in
single quotation marks to prevent uppercasing:
We code the keyword system because Regina Rexx defines this string as its standard identifier for the
operating system (regardless of what the underlying OS may be). Other Rexx interpreters may require
other strings under various operating systems (see Table L-2 in Appendix L).
The keyword with tells Rexx that one, two or three redirections will follow, identified by the keywords
input, output, and error. These three keywords may appear in any order. Those that are not coded
take defaults. Since we want to send input to the sort command from a file, we coded keyword stream,
followed by the filename, sortin.txt.
Coding output stream tells Rexx to send the command output to the file named sortout.txt. Rexx is
case-insensitive, so the case of keywords like with input stream is irrelevant to the interpreter. We
used mixed case here simply to clarify the address instruction keywords.
218
Issuing System Commands
output and error streams can either replace or be appended to the named files. Use the keywords
replace or append to denote this. replace is the default. Here is the same example that was given ear
lier, but with the provision that the output stream will be added (appended) to, instead of replaced:
Recall that the comma (,) is the continuation character. We’ve coded it here simply to continue this long
instruction across lines. We also placed single quotation marks around the filenames. The instruction
works without them but then the filenames will be altered to uppercase. Whether this is desirable
depends on the operating system. Operating systems like Linux, Unix, and BSD are case-sensitive in
their file-naming convention; operating systems like Windows and DOS are not.
Since we’ve specified append on the address instruction’s output stream, if no output file named
sortout.txt exists, running this instruction results in an output file containing:
perl
php
python
rexx
Running the command a second time appends to the output file for this result:
perl
php
python
rexx
perl
php
python
rexx
The replace option would always give the same result that is listed first in this example. In other
words, the replace option replaces any existing file with the results, while append will add results to
the end of an existing file.
You can mix file I/O and array I/O in the same address instruction. This example provides input via an
array but writes the output to a file to give the same results as the previous examples:
address SYSTEM sort WITH INPUT STEM in_array. OUTPUT STREAM ‘sortout.txt’
You mustplace the number of input array lines in element 0 of that array priorto executing the address
instruction or it will fail. Of course, you also place the elements to pass in to the command in the array. If
you specify array output, Rexx communicates to your program how many output and error lines are
produced by filling in array element 0 with that value.
219
Chapter 14
The ANSI-1996 standard added several parameters you can specify on the address function to retrieve
specific address instruction settings. The following table lists the address function options:
Here is an example. This say instruction displays the defaults for each source or target:
What this displays will be system and interpreter dependent. Here’s an example of output for Regina
Rexx running on a Windows computer:
Input source: INPUT NORMAL Output target: REPLACE NORMAL Error target: REPLACE NORMAL
The address function, then, is the basic means through which a script can get information about
where its commands will be issued and how their I/O is controlled.
This example sends a command to the KEDIT program, a text editor. Of course, what environments
are available (and how you refer to them in the environment string), strictly depends on your platform
and the available interfaces. Typical command interfaces are for program and text editors, network
control, teleprocessing monitors, and the like.
220
Issuing System Commands
There are two basic ways to tell Rexx where to send commands to. We’ve seen one — code the environ
ment string on the address instruction. Another way is to issue the address instruction with an
environment specified but lacking a command. This sets the command target for all subsequent commands.
Look at these commands run in sequence:
Using this form of the address instruction has the advantage that you can code shorter, more
intelligible commands. But explicitly coding a command on the address instruction along with its
environment better documents where the commands are sent for execution. Personal preference dictates
which to use.
You might also see the address instruction coded without any target:
address
In this case, the instruction causes commands to be routed to the environment specified prior to the last
time it was changed. In other words, repeated coding of address without any environment operand
effectively “toggles” the target for commands back and forth between two target environments.
While this could be appropriate and convenient for certain situations, we do not recommend this
approach. It becomes confusing; we prefer one of the two more self-documenting approaches described
previously.
Remember that you can always code the address function to determine the target environment for
commands:
Finally, we mention that the address instruction requires that the environment must be a symbol or
literal string. It cannot be a variable. As in certain other Rexx instructions, code the value keyword if
you need to refer to the environment as a value contained in a variable:
environment_variable = 'SYSTEM'
address value environment_variable /* sets the environment to SYSTEM */
Sending a variable parameter into the address instruction provides greater flexibility than hard
coding and allows scripts to dynamically change the target of any commands.
221
Chapter 14
A Sample Program
You now know the basic techniques for issuing operating system commands and managing their results.
Let’s look at a sample program that shows some of these techniques in action.
This program provides command help information for a Windows computer. First it issues the Windows
help command to the operating system without any operands. The command would be issued like this:
help
This command outputs a list of operating system commands with one line of description for each. The
output looks similar to this:
The script captures this output and places it into an array. The command name is the index; the com
mand description is stored in the array at that position. For example, the subscript ASSOC holds the line
of help information on that command, the array element with subscript AT contains a line of
information on that command, and so on.
(Note that a few commands contain more than one line of description. This program ignores the second line
for those few commands.)
After building the array of command help information, the script prompts the user to enter an operating
system command. In response, the script displays the one-line description for that command from the
array. It also asks the user if he wants more detailed command information. If the user responds yes or
y, then the program issues the help command for the specific command the user has chosen to the
operating system. For example, if the user wants more information on the dir command, the script
issues this Windows command on the user’s behalf:
help dir
This OS command displays more extensive information about the command and its use. Several lines of
command help information appear as well as a listing of command options or switches.
After the user views the verbose help information on the command, the program prompts him to enter
another command about which he needs information. The user either enters another OS command
seeking help information, or he enters quit and the program terminates.
222
I ssuing System Commands
Enter Command you want Help on, or QUIT: vol
VOL Displays a disk volume label and serial number.
Want detailed information? y
Displays the disk volume label and serial number, if they exist.
VOL [drive:]
Enter Command you want Help on, or QUIT: ls
LS No help available.
Enter Command you want Help on, or QUIT: quit
/* COMMAND HELP: */
/* */
/* (1) Gets HELP on all OS commands, puts it into an array. */
/* (2) Lets user get HELP info from the array or the OS. */
/* read contents of CMD TEXT OUT, build help array CMD HELP */
/* allow user to query CMD HELP array & issue full HELP commands */
call charout ,"Enter Command you want Help on, or QUIT: "
pull cmd in .
do while cmd in <> 'QUIT'
if cmd help.cmd in = '' then
say cmd in ' No help available.'
else do
say cmd in cmd help.cmd in
call charout ,"Want detailed information? "
pull answer .
if answer = 'Y' | answer = 'YES' then
address SYSTEM 'help' cmd in
end
call charout ,"Enter Command you want Help on, or QUIT: "
pull cmd in .
end
This script first sets the trace off, because issuing a valid help command under many Windows
versions sends back a return code of 1. This contravenes normal operating system convention and
means that if the script does not mask the trace off, the user will view error messages after the script
(correctly) issues Windows help commands.
223
Chapter 14
Then the script initializes all elements in its two arrays to the null string:
The subsequent address instruction gets output from the Windows help command into the array
named cmd_text_out:
This instruction does not specify input to the help command because it intends to issue the help
command without operands or any input. The output from the help command goes into the array
cmd_text_out. Each element in this array contains an OS command and one line of help information.
The script needs to break apart the OS command from its single line of help information. The following
code does this and builds the new array cmd_help.
do j=1 to cmd_text_out.0
parse var cmd_text_out.j the_command command_desc
cmd_help.the_command = command_desc
end
The cmd_help array contains one description line per Windows command. Its index is the command
itself — it is an associative array, as explained in Chapter 4. Once the array is built, the next step is to
prompt the user to enter the operating system command about which he wants help. This statement
causes the prompt to appear on the user’s screen:
call charout ,"Enter Command you want Help on, or QUIT: "
The program then uses the command the user enters as an index into the cmd_help array. This
statement applies that command as the index into the array and displays the associated line of help
information to the user:
Now, the script presents the user with a choice. Either he can ask for full information about the OS
command about which he is inquiring, or he can say “no more” and ask about some other OS command.
These statements prompt the user as to whether he wants more information about the current command:
If the user answers YES to this prompt, the script then issues the Windows help command with the OS
command of interest as its operand. The generic format for this Windows help command is this:
help command
224
I ssuing System Commands
For example, if more information were desired about the Windows dir (directory) command, the script
would issue this command to Windows:
help dir
As another example, if more information were needed about the ver (version) command, the script
would issue this Windows command:
help ver
This form of the help command prompts Windows to display several lines of detailed help
information on the screen. This is the line in the program that actually issues the extended help
command:
The variable cmd_in is the command the user wants detailed information about. So, if the user requests
information on the dir command, this statement resolves to:
Since SYSTEM is Regina Rexx’s default command environment, the program did not need to explicitly
encode the address instruction. This line would have given the same result:
'help' cmd_in
This statement is less cluttered than coding the full address instruction, but one must know what the
default command environment is to understand it. Some developers prefer to code the address
instruction in full to better document their code. Others prefer to issue operating system commands
without it for the sake of brevity.
For many years in mainframe Rexx, the external data queue or stack was used for communications
between Rexx scripts and the commands they executed. (You’ll recall that Chapter 11 discussed the
stack and its use in some detail and presented several sample scripts that make use of it.) Because
mainframe Rexx was the first available Rexx, and because the ANSI-1996 standard was devised rather
late in the Rexx evolution, most Rexx interpreters also support the stack for command I/O.
Regina Rexx is typical in this regard. It fully supports the ANSI-1996 standard address instruction
with its ANSI keywords for stream and stem I/O. But in recognition of the historical importance of
mainframe Rexx, it alternatively allows you to use the stack for command I/O. This fits with Regina’s
philosophy of supporting all major standards, both de facto and de jure.
225
Chapter 14
Using the stack for command I/O via its keywords FIFO and LIFO is an “allowed extension” to the
ANSI-1996 standard. FIFO and LIFO stand for "first-in, first-out" and "last-in, first-out", respectively.
Chapter 11 introduced these concepts along with their common uses for the stack.
Remember the example of how to code the sort using redirected input and output? The OS command we
originally coded within a script was:
This code implements the same result by using the stack in Regina Rexx:
The code places four unsorted items into the stack through the queue instruction. The address
instruction uses the keywords input fifo to send those four items in unsorted order to the sort
command. The output fifo keywords retrieve the four sorted items from the sort command in
FIFO order. The two back-to-back single quotation marks that appear in these clauses mean that the
default stack will be used:
Rexx clears or flushes the stack between its use as an input to the command and its role as a target to
collect the command output. The interpreter endeavors to keep command input and output accurate
(not intermixed).
The do loop at the bottom of the script displays the four sorted items on the user’s screen:
perl
php
python
rexx
We recommend using the ANSI-1996 standard address instruction and its keywords with, input,
output, error, stream and stem. This is more portable than using the stack and is becoming more
widely used. But either approach will work just fine.
226
I ssuing System Commands
Summary
Operating system commands bring great power to Rexx scripts, while scripting brings programmability
and flexibility to operating systems. This chapter describes how Rexx scripts issue commands to the
operating system and other target environments. It shows how to verify success or failure of these
commands by checking their return codes, as well as other techniques to analyze command results. It
also describes the several methods by which input can be sent to those commands, and through which
command output and errors are captured.
Rexx implementations traditionally use the stack for command I/O, but developers increasingly favor the
ANSI-1996 standard approach. This chapter illustrates both methods and showed several code examples.
We looked at the ways in which operating system commands can be assembled and submitted to the OS
for execution. We investigated how scripts know whether commands succeed, and ways to inspect their
error output. Then we explored the address instruction, the basic vehicle by which command I/O can
be intercepted, managed, and controlled, and the address function, which returns information about
the command execution environment. Finally, we showed how to use the stack for controlling
command I/O. Many Rexx scripts use the stack for command I/O instead of the ANSI-1996 compliant
address instruction.
227
Interfacing to Relational
Databases
Overview
Many scripts are only useful when the scripting language interfaces to a wide variety of packages.
For example, scripts need to control graphic user interfaces, or GUIs, perform database I/O, serve up
customized Web pages, control TCP/IP or FTP connections for communications, display and
manipulate images, and perform many other tasks that require interfaces to outside code.
Rexx offers a plethora of free and open-source interfaces, packages, and function libraries for these
purposes. Appendix H lists and describes many of the more popular ones. This chapter explores
how scripts interface to databases. Then Chapters 16 through 18 explore other free interfaces for
Rexx scripts, including GUIs, graphical images, programming Web servers, and Extensible
Markup Language (XML).
Databases are among the most important interfaces. Few industrial-strength programs can do
without the power and data management services of modern database management systems, or
DBMS.
Most database systems are relational. They view data in terms of tables composedof low's and
columns. Relational DBMSs typically provide complete features for data management, including
transactional or concurrency control, backup and recovery, various utilities, interfaces, query lan
guages, programming interfaces, and other features for high-powered data management.
Several packages enable Rexx scripts to interface with databases. These allow the scripts to inter
face to almost any DBMS, including both open-source and commercial systems. This chapter
focuses on the most popular open-source database interface, Rexx/SQL. Rexx/SQL interfaces
Rexx scripts to almost any database. Among them are open-source databases like MySQL,
PostgreSQL, Mini SQL, mSQL, and SQLite, and commercial databases like Oracle, DB2 UDB, SQL
Server, and Sybase. A full list follows in the next table.
Chapter 15
Rexx/SQL has been production tested with several different Rexx interpreters, including Regina and
Open Object Rexx. The examples in this chapter were all run using Regina Rexx with Rexx/SQL.
Rexx/SQL supports two types of database interface: custom, or native,interfaces andgeneric database
interfaces. Native interfaces are DBMS-specific. They confer performance advantages but work with only
one database. Generic database interfaces work with almost any database and offer greater standardiza
tion and more portable code—but at the possible cost of lesser performance and the exclusion of non
standard features.
□ Widulyused
□ Oj^eiu rource
□ Intcrfucedtomll major databases
□ Confodms todataeasdstandards
The sample scripts in this chapter all nun against the MySQL open-source database. MySQL is the most
widely used open-source database and we used it for the database examples because it fits with the
book’s emphasis on Cree and open-source soCtware. White the sampte scripts use the MySQL interCace,
they could run against databases other than MySQL with very minor modifications. In most cases, only
the first database function call (sqlconnect) would have to be altered in these sample scripts to run
them against other databases. The latter part of this chapter explains how to connect scripts to other
popular databases, including Oracle, DB2 UDB, and Microsoft SQL Server. The chapter’s sample scripts
were tested on a Windows server running MySQL.
Rexx/SQL Features
The Rexx/SQL database interface follows the two major database standards for a call-level interface, or
CLI. These two interface standards are the Open Database Connectivity Application Programming Interface,
or ODBC API, and theX/Open CLI.
Rexx/SQL supports all expected relational database operations and features. It executes all kinds of SQL
statements, including Data Definition Language, or DDL, and Data Manipulation Language, or DML. Data
definition statements create, alter, and remove database objects. They include, for example, create
table or drop index. Data manipulation statements operate on rows of data, and include such state
ments as select, insert, update, and delete.
Rexx/SQL provides all the features and functions with which you may be familiar from the call-level
interface of any database. If you are familiar with the CLI used by Oracle, DB2 UDB, MySQL, or any
almost any other database, you will find Rexx/SQL easy and convenient. Rexx/SQL supports features
such as cursor processing, dynamic statement preparation, bind variables and placeholders, SQL control
structures such as the SQL Communications Area(SQLCA), SQL error messages, null processing, auto
commit options, concurrent database connections, and the retrieval and setting of database interface
behaviors.
If you have no experience with call-level database processing, this chapter offers an entry-level tutorial.
It will help you download and install Rexx/SQL and write simple Rexx scripts that process the database.
Ultimately, you may want to pursue the topic of database processing further by reading about the ODBC
or X/Open CLIs.
230
Interfacing to Relational Databases
Both source and binaries appear as downloadable for various platforms. For example, Windows users
can download a *.zip file. Decompressing that file effectively installs the product. Linux, Unix, and
BSD users can download *.tar.gz files. Source is available in *src.zip files.
Download the file specific to the database to which your Rexx scripts will connect, or download the
generic ODBC driver. Look for these keywords within the download filename to tell you which database
it supports, as outlined in the that follows.
Keyword Supports
ORA Oracle
DB2 Db2 (Db2 UDB)
SYB Sybase
SAW Sybase SQL Anywhere
MIN Mini SQL (mSQL)
MY MySQL
ODBC Generic ODBC driver
UDBC Openlink UDBC interface
SOL Solid Server
VEL Velocis (now Birdstep)
ING Ingres
WAT Watcom
INF Informix
POS Postgres and PostgreSQL
LITE SQLite
PRO Progress
Rexx/SQL uses these abbreviations throughout the product whenever there is a need to provide a stan
dard moniker for a database. Of course, updates and changes may occur to the list so check the
Rexx/SQL home page documentation to determine which databases are fully supported.
As an example, let’s interface Rexx to a MySQL database under 64-bit Windows. Download either a file
named rxsql_ odbc_w64.zip or one named rxsql_ my_w64.zip. Some of the underscores in these
sample filenames are replaced by the version number of the product. For example, depending on the
version number, a real filename might be something like rxsql24_odbc_w64.zip. The filenames show
231
Chapter 15
which database the driver supports. For example, rxsql24_odbc_w64.zip supports the generic ODBC
driver, while rxsql24_my_w64.zip supports MySQL. We chose the native MySQL interface for the
examples in this chapter, but either works fine and provides the same results.
Under Windows, if you use an ODBC driver, you must use Windows’ODBC Data Sources Administrator
Tool to register the ODBC driver with Windows. On Windows, access this panel through Start |
Settings | Control Panel | Administrative Tools | Data Sources (ODBC). Or use the Help function and
search for keyword odbc.
If you’re not using Windows, or if you’re on Windows but are using a native interface, registering the
driver via the ODBC Data Sources Administration Tool is not necessary. Since we use the native MySQL
interface for the examples in this chapter, we did not have to use the ODBC Data Sources Administrator
Tool.
The Rexx/SQL download will decompress to include files with names starting with the letters readme.
For example, in downloading the ODBC driver for Windows, we saw the two files readme.odbc and
readme.rexxsql among the extracted files. Read these files! They tell you all you need to know about
setting up Rexx/SQL for your particular platform.
On some platforms, you may need to finish the installation by setting a couple of environmental vari
ables. The path variable on many operating systems might need to include the directory where Rexx/
SQL is installed. The readme file will tell you if any other actions are required.
For example, on Windows systems, the path should be set to include the folder in which the Rexx/SQL
Dynamic Link Library, or DLL, file resides, named rexxsql.dll. For Linux, Unix, and BSD systems, the
directory in which the shared library file for Rexx/SQL resides must be pointed to by the environmental
variable that references library files. On Linux this environmental variable is named ld_library_path.
See the readme file for this name for other Unix-derived operating systems.
Once Rexx/SQL is installed, run the product’s test script named simple.cmd. It resides in the directory
in which Rexx/SQL installs. This test program simply connects, then disconnects, from the database for
which you installed Rexx/SQL. It lists descriptive error messages in the event of any problem.
Next run the product test script named tester.cmd. This is a more ambitious test script that creates
some tables, makes multiple database connections and disconnections, and runs a wide variety of com
mon SQL statements on the database. The documentation at the top of this script gives you advice about
how to run it. You must set an environmental variable or two, the nature of which varies by the database
you use. Read the documentation at the top of the script for all the details — prior to running the script.
If the two test scripts work, Rexx/SQL is installed successfully on your system. Incidentally, the two test
scripts provide excellent examples of how to code for Rexx/SQL in your scripts. Along with the sample
scripts in this chapter, you can use them as coding models to get started with Rexx/SQL and database
scripting.
The Basics
The Rexx/SQL database interface follows the two major database standards for a CLI: the ODBC API
and the X/Open CLI. Interfacing with the database via the CLI means issuing a series of database or SQL
calls. Rexx/SQL supplies these as a set of functions. The series of calls a script issues depends on the
type of database activity the script performs.
232
Interfacing to Relational Databases
Let’s discuss the Rexx/SQL database functions organized by functional area. Our goal here is to give
you an overview of what these functions are, what they do, and how to apply them. (Appendix F
describes all the functions in full detail. The appendix lists the functions alphabetically along with their
full coding formats and coding examples.)
Database Connections. These functions enable scripts to connect to a specified database, manage that con
nection, and disconnect from the database when SQL processing is completed.
Environmental Control. While there is but a single function for environmental control, it has an important
role in database programming. This function allows scripts to either query or set various runtime values
that affect their interactions with the database.
Issuing SQL Statements. These functions enable scripts to issue all kinds of database calls, including data
definition and data manipulation statements. SQL statements can be executed by a single Rexx state
ment, or they can be prepared in advance and executed repeatedly and with optimal efficiency. These
functions also allow scripts to process multiple-row result sets either with cursors or other techniques
for multi-row processing.
□ sqlprepare — Allocates a work area for a SQL statement and prepares it for processing
Transaction Control. The two transaction control statements permit scripts to dictate when data changes
are permanently applied to the database. Transaction control is fundamental to how databases guarantee
data integrity and their ability to recover a database, if necessary.
We’ll see examples of many of these SQL functions in the sample scripts that we will now discuss.
233
Chapter 15
The first sample database script performs several “startup” and “concluding” actions that are common
to all database scripts. The only real action it takes once it connects to the database is to report some
environmental information it retrieves about the databaase. Here is what this first sample database
script does:
Figure 15-1 describes these actions diagrammatically as a flowchart. With the addition of database pro
cessing logic, this is the skeletal structure of most SQL scripts.
Figure 15-1
Here’s what the output from the first sample script looks like. You can see it just displays some basic ver
sion information about the Rexx/SQL interface along with environmental information it retrieved from
the database:
The Rexx/SQL Version is: rexxsql 2.4 02 Jan 2000 WIN32 MySQL
The database Name is: mySQL
The database Version is: 4.0.18-max-debug
234
Interfacing to Relational Databases
/****************************************************************/
/* DATABASE INFO: */
/* */
/* Connects to MySQL, displays database information. */
/****************************************************************/
signal on syntax /* capture SQL syntax errors */
if SQLGetinfo(,’DBMSNAME’,’desc.’) <> 0
then call sqlerr ‘Error getting db name’
else say ‘The database Name is: ‘ desc.1
if SQLGetinfo(,’DBMSVERSION’,’desc.’) <> 0
then call sqlerr ‘Error getting db version’
else say ‘The database Version is: ‘ desc.1
exit 0
/* capture any SQL error and write out SQLCA error messages */
235
Chapter 15
The first step in this program is to load the Rexx/SQL external function library and make its functions
available for the use of this script. Regina uses the Systems Application Architecture, or SAA, standard
functions to achieve this. Here is one way of coding them:
The rxfuncadd function first loads or registersthe sqlloadfuncs function. The middle parameter spec
ifies the name of the file in which sqlloadfuncs can be found. In Windows, this external library is a
Dynamic Link Library, or DLL, file. It is named rexxsql.dll. The directory in which this file resides
should be part of Windows’ path environmental variable so that Regina can locate it.
Under Linux, Unix, and BSD, the equivalent of a Windows DLL is a shared library file. An environmental
variable specifies the directory in which this shared library file resides. Different versions of Unix use
different environmental variable names for this purpose, so check the readme* file for the details for
your Unix version. On most systems, it will be named ld_library_path or libpath. On Linux sys
tems, this environmental variable is ld_library_path.
To reiterate, the rxfuncadd statement registers the function sqlloadfuncs, which is part of the Rexx/
SQL external library. The call to sqlloadfuncs then loads the remainder of the Rexx/SQL external
library. Now all its functions are available for the use of this script. See Chapter 20 if you want more
detail on the functions to access external libraries.
Since all scripts that interface to databases use this code, consider placing it in a Rexx script function or
subroutine. This takes it out of line for the main body of the code and simplifies your program.
Once the script loads the Rexx/SQL external function library, it can connect to the database. Here we
connect to the MySQL database named mysql (one of the two databases MySQL creates by default when
installed):
The sqlconnect statement can take several other parameters, as shown in its template diagram:
The required parameters for this statement vary by the database with which you are trying to connect. Our
example only supplies the name of the database to which the script wishes to connect. sqlconnect is just
about the only statement in Rexx/SQL whose coding is database-dependent. The section entitled “Working
with Other Databases” later in this chapter discusses and illustrates how to code the sqlconnect function
for systems like Oracle, DB2 UDB, SQL Server, and ODBC connections.
236
Interfacing to Relational Databases
After connecting, the script executes the sqlvariable function to retrieve and display the version of
Rexx/SQL:
Then the script invokes the sqlgetinfo function twice, with different parameters, to retrieve the DBMS
name and version:
if SQLGetinfo(,’DBMSNAME’,’desc.’) <> 0
if SQLGetinfo(,’DBMSVERSION’,’desc.’) <> 0
Rexx/SQL places the results into the stem variable named in the call. Here this stem variable is desc.,
so the output strings we want are in the variable named desc.1. The full statements show how these
values are retrieved and displayed:
if SQLGetinfo(,’DBMSNAME’,’desc.’) <> 0
then call sqlerr ‘Error getting db name’
else say ‘The database Name is: ‘ desc.1
if SQLGetinfo(,’DBMSVERSION’,’desc.’) <> 0
then call sqlerr ‘Error getting db version’
else say ‘The database Version is: ‘ desc.1
Its work done, the script disconnects from the database and drops the Rexx/SQL function library from
memory. Scripts typically perform these two steps as their final database actions. Here is the code that
implements these two terminating actions:
We’ve nested the functions inside of if instructions in order to check their return codes. When perform
ing database processing, we recommend alwayschecking return codes from the database functions. If not
for this little bit of extra code, the application could otherwise behave in ways that completely mystify
its users, even when the problem is something so simple as a database that needs to be started up. It is
standard practice in the database community to check the return code from every SQL statement in a
program.
When database function errors occur, this script executes this internal routine:
237
Chapter 15
Here’s an example of the kind of error message this routine might output. In this case, the sqlconnect
function failed because an incorrect database name was supplied. The database name was incorrectly
specified as mysqlxxxx instead of as mysql:
In this example, the error routine displays the SQL error message and stops the program (the last state
ment in the error routine is an exit instruction). You could write the routine to take any other appropri
ate action, as you see fit, and continue the program. You might even choose whether to end the program
or continue it, depending on the nature and severity of the error the error routine encounters.
The error routine shows how to retrieve and display various error messages from the database. Its first
line gives its full access to the SQL Communications Area, or SQLCA:
The SQLCA is the basic data structure by which the DBMS passes status information back to the pro
gram. The status values in the SQLCA set by database activity include the following:
Database scripts can either handle SQL errors in the body of the code (inline), or they can consolidate
error handling into one routine, such as the sqlerr routine in the sample script. In large projects consol
idating code is advantageous because it leads to code reuse, standardizes error handling, and reduces
the size and complexity of the inline code.
The syntax error condition trap fits right in with the sqlerr routine in capturing and handling SQL
syntax errors. It is very easy to make syntax errors when coding to the SQL CLI because the character
strings one issues to the database become complicated. The syntax error condition trap manages this
challenge:
238
Interfacing to Relational Databases
For large applications, we recommend writing a single SQL error-handling routine and having all SQL
errors sent to that routine. The syntax trap routine can also call the SQL error handler, if desired. This
sample script simplifies error handling for clarity of illustration. The test scripts distributed with
Rexx/SQL provide a fuller and more robust SQL error handler. Review those scripts if you want to
develop a more comprehensive, generalized SQL error handler.
This program creates a phone directory. It does this by creating a database table named phonedir, then
loading it with data. The “data load” is simply an interactive loop that prompts the user to enter peo
ple’s names and their phone numbers. When the user enters the character string exit, the program
ends.
/****************************************************************/
/* PHONE DIRECTORY: */
/* */
/* Creates the phone directory and loads data into it. */
/****************************************************************/
signal on syntax /* capture SQL syntax errors */
call sql_initialize /* load all Rexx/SQL functions*/
239
Chapter 15
The first line of the program enables the syntax error condition:
Since the previous sample script, Database Info, already showed the code for the syntax error handler,
we have not shown it again in the above program. Similarly, the next line in the script invokes a new
subroutine called sql_initialize:
This routine registers and loads the Rexx/SQL interface. It contains exactly the same code as the previ
ous program (using the rxfuncadd and sqlloadfuncs functions). We do not duplicate this code in this
example, in order to keep it as short and readable as possible.
After connecting to the database, the script tells MySQL which database it wants to use. It issues the
MySQL use test database command through a single call to the sqlcommand function:
For purposes of initialization, the script drops the phonedir table if it already exists. If the phonedir
table does not exist and this statement fails, that’s okay. We’re only dropping it to ensure that the subse
quent create table statement will not fail because the table already exists. (The pairing of drop
table / create table statements in this manner is a common technique in database processing.)
Here is the drop table statement:
The statements that create the database table phonedir come next:
As this code shows, the new table has only two columns: one for the person’s last name and one for their
phone number. The first statement builds the SQL create table statement in a variable, while the sec
ond statement executes that command. The second statement also references the sqlerr routine,
because we consolidated all SQL error processing in a single routine. Since this routine contains the exact
same code as the previous sample program, we have not included it in the program’s code here.
The script now enters a do loop where it prompts for the user to enter names and their associated phone
numbers. These two statements build and issue the SQL insert statement that adds each record to the
database:
240
Interfacing to Relational Databases
Instead of building the SQL statement separately, in the first statement, it could be nested inside of the
sqlcommand function call. We use a separate statement to build this string because of its syntactical com
plexity. This makes for more readable code. A large percentage of SQL programming errors involve
statement syntax, and this approach makes it easy to verify the SQL statement simply by displaying the
string. We generally recommend building SQL statements in variables like this rather than dynamically
concatenating them within the actual SQL function encoding.
To end the program, we need to issue the sqldisconnect and sqldropfuncs calls. Since this code is
the same as the previous program, we’ve isolated it in its own routine called sql_pgm_end:
We don’t include this code in the example because it duplicates the same lines as the previous sample
script. You can see that using common routines for database connection, disconnection, and error han
dling is a very sensible approach. It both reduces the code you must write for each script and reduces
errors.
In database programming, scripts must commit (make permanent) any database changes. In this script,
the data is auto-committedto the database by disconnecting from the interface. Auto-commit automati
cally commits the data to the database if the script ends normally. Alternatively, the script could explic
itly issue the SQL sqlcommit statement:
The Rexx/SQL interface allows scripts to control the auto-commit feature. Use the sqlvariable func
tion to retrieve and/or set this and other behaviors of the database interface. Simple programs like this
sample script tend to rely on auto-commit to apply changes to the database upon their termination.
More advanced database scripts require explicit control of commit processing. We’ll see an example of
the sqlcommit function later in this chapter in a script that updates the phone numbers in the database.
/****************************************************************/
/* PHONE DIRECTORY LIST: */
/* */
/* Displays the phone directory’s contents. */
/****************************************************************/
signal on syntax /* capture SQL syntax errors */
call sql_initialize /* load all Rexx/SQL functions*/
241
Chapter 15
do j = 1 to sqlca.rowcount
say ‘Name:’ s1.lname.j ‘Phone:’ s1.phone.j
end
This script uses some of the same database service routinesas the previous example:
The code for these routines is in the first sample program and is not repeated here. The basic problem in
this script is this: how do we execute a SQL select statement to retrieve and display the rows in the
table? Here is the code that builds and executes the select statement:
This statement retrieves the data of the rows in the phonedir table. To display it, we need a do loop:
do j = 1 to sqlca.rowcount
say ‘Name:’ s1.lname.j ‘Phone:’ s1.phone.j
end
Statement_name.Column_name.Row_identifier
s1.lname.j
This neat Rexx/SQL syntax makes multiple row retrieval easy. Just put this row reference inside a do
loop and display all the data. Later we’ll see another way to display data from a SQL select statement
via standard ODBC-X/Open programming, called cursor processing.
The variable sqlca.rowcount was set by the interface as feedback to the select statement. It tells how
many rows were retrieved by the select, so we use it as the loop control limit. Another way to get this
same information is to inspect element 0 in the returned rows. s1.lname.0 and s1.phone.0 also
242
Interfacing to Relational Databases
contain a count of the number of rows retrieved. Instead of referring to sqlca.rowcount, as does the
preceding code, we could also have coded the display loop as:
do j = 1 to s1.lname.0
say ‘Name:’ s1.lname.j ‘Phone:’ s1.phone.j
end
Either approach to controlling the number of do loop iterations works fine. Once retrieval and display of
the rows is complete, the script calls its internal routine sql_pgm_end to disconnect from the database
and drop the Rexx/SQL functions. This terminates the database connection and releases resources
(memory).
This simple script updates the phone numbers. Its do loop prompts the user to enter a person’s name. If
the person exists in the table, the program prompts for a phone number, and updates that person’s
phone number in the phonedir table. If the person does not exist in the table, the script displays a “not
found” message and prompts for the next person to update. The script ends when the user enters the
character string exit.
/****************************************************************/
/* PHONE DIRECTORY UPDATE: */
/* */
/* Updates rows in the phone directory w/ new phone numbers. */
/****************************************************************/
signal on syntax /* capture SQL syntax errors */
call sql_initialize /* load all Rexx/SQL functions*/
243
Chapter 15
Much of the code of this program is similar to what we’ve seen in previous examples. Among the new
statements, this is the select statement that tries to retrieve the phone number of the person the user
enters:
If the variable sqlca.rowcount is not 1 after this call, we know that we did not retrieve a row for the
name. The person (as entered by the user) does not exist in the table:
The script assumes that each person’s name is unique, so the statement will either retrieve 0 or 1 rows.
Of course, in a real database environment, some unique identifier or keyother than the person’s name
would likely be used.
If we do retrieve a row, this code prompts the user to enter the person’s new phone number and updates
the database:
After the SQL update statement, the program commits any changes made to apply them permanently to
the database through the sqlcommit function:
244
Interfacing to Relational Databases
Some interfaces do not allow SQL select statements that return more than one row to be run through a
single statement. If a select statement returns more than one row, it requires cursor processing. Acursor
is a structure that allows processing multiple-row result sets, one row at a time.
These are the major steps in processing multi-row results sets using a cursor:
1. A sqlprepare statement prepares the cursor for use. This allocates a work area and “compiles”
the select statement associated with the cursor.
2. The sqlopen function opens the cursor.
3. A program do loop retrieves rows from the cursor, one by one, through the sqlfetch function.
4. When done, the script closes the cursor by a sqlclose, and deallocates the work area by a
sqldispose call.
SQLPrepare
SQLOpen
SQLFetch
SQLClose
SQLDispose
Figure 15-2
245
Chapter 15
This script implements the logic of cursor processing:
/****************************************************************/
/* PHONE DIRECTORY LIST2: */
/* */
/* Displays the phone directory’s contents using a cursor. */
/****************************************************************/
signal on syntax /* capture SQL syntax errors */
call sql_initialize /* load all Rexx/SQL functions*/
In this program, the sqlprepare function allocates memory and internal data structures and readies the
SQL statement (here a select) for subsequent execution:
Next, open the cursor by a sqlopen statement. Cursors must always be explicitly opened, as this next
statement shows. In this respect, cursors are not like Rexx files, which are automatically opened for use:
Once the cursor is open, fetch and display rows from the cursor, one at a time, by using the sqlfetch
call. This do loop shows how individual rows may be processed, one after another:
After all the rows have been processed, end by closing the cursor and freeing any resources. Use the
sqlclose and sqldispose functions for this:
246
Interfacing to Relational Databases
Statement preparation can be used for other SQL statements besides select. While this approach may
seem more cumbersome, it offers a performance benefit if the script executes the SQL statement more
than once. This is because the sqlprepare function places SQL statement compilation into a separate
step. Executing the SQL statement is then a separate, more efficient, repeatable step. If you prepare a
SQL statement one time, then execute it repeatedly, this multi-step approach yields better performance.
SQL insert, update and delete statements can also be prepared in advance. Use the sqlexecute
function after the sqlprepare function to execute the SQL insert, update or delete statement. End
the process by sqldispose. Use the same sequence of statements for data definition statements:
sqlprepare, sqlexecute, sqldispose. (One DDL statement, describe, requires this sequence:
sqlprepare, sqldescribe, sqldispose).
Rexx/SQL gives you the choice whether to opt for convenience with the single-statement processing of
the sqlcommand function, or to go for performance with sqlprepare and sqlexecute. The trade-off
between the two approaches is one of coding convenience and simplicity versus optimal performance.
Bind Variables
Structured Query Language, or SQL, permits the kinds of database queries illustrated by the sample
programs above. But if SQL statements were always hardcoded, the language would not offer the pro
grammability or flexibility scripts require. Bind variables provide the required flexibility. Bind variables
are placeholders within SQL statements that allow scripts to dynamically substitute values into the SQL
statement.
Here’s an example. In the phone directory update script, we prompted the user to enter a person’s
name; then we retrieved the phone number based on that name. We then dynamically concatenated that
person’s name into the SQL select statement the script issued. The statements worked fine but the
dynamic concatenation made for some complex syntax. Here’s a simpler way to write the same state
ments using a parameter markeror placeholder variablethat represents the bind variable
The question mark (?) is the placeholder variable. The sqlcommand function includes an extra parame
ter that supplies a value that will be dynamically substituted in place of the placeholder variable prior to
SQL statement execution. In this example, the value of lname will replace the placeholder variable
before execution.
Bind variables can be a more efficient way to process SQL statements. They also are a little easier or
cleaner to code. Rexx/SQL fully supports them. But different DBMSs have different syntax for parame
ter markers and so this feature is necessarily database-dependent. We eschewed programming with bind
variables in this chapter for this reason.
247
Chapter 15
The one statement that does change when targeting different databases is sqlconnect. Database con
nections are inherently DBMS-specific. The next three brief sections describe the basic rules for encoding
SQLConnect statements to access Oracle-, DB2 UDB-, and ODBC-compliant databases. The ODBC
drivers, as explained earlier in this chapter, permit scripts to access almost any database, because ODBC
is widely implemented as a universal interface for database access. You would use the ODBC drivers
when connecting to Microsoft SQL Server databases, for example.
Connecting to Oracle
Here’s how to connect to Oracle databases using the Rexx/SQL package. When connecting to Oracle
databases via the sqlconnect function, all sqlconnect parameters are optional. Here are some sample
connections. To connect to a database running on the local machine with an externally dentified userid
and password:
rc = sqlconnect()
To connect to a local database with the default userid of scott with its default password of tiger:
Now let’s connect scott to a remote database on machine prod.world (as identified in Oracle’s
SQL*Net configuration files):
rc = sqlconnect(‘MYCON’,’scott’,’tiger’,,’PROD.WORLD’)
The database nameparameter is required for a DB2 connection. Here are some sample connections. To
connect to the sample database and name the connection mycon, encode this:
rc = sqlconnect(‘MYCON’,,,’SAMPLE’)
248
Interfacing to Relational Databases
To connect as coder with the password of topgun,you would code a statement like this:
rc = sqlconnect(,'CODER','TOPGUN','SAMPLE')
The Db2 UDB database fully supports bind variables. Db2 bind variables are denoted by the standard
marker, the question mark (?).
In making the ODBC connection, the userid, password, and database name arguments are required on the
sqlconnect function. Here is an sample connection:
rc = sqlconnect('MYCON','scott','tiger','REXXSQL')
The connection is named mycon and the login occurs using userid scott and its password tiger. The
fourth argument is the ODBC Data Source Name, or DSN. Under Windows systems, this is created using
the Window’s ODBC Data Sources Administration tool. The DSN in the preceding sample statement is
named rexxsql.
Connecting to MySQL
MySQL is one of the most popular open source database in the world. Like Rexx itself, it is freely
downloadable and highly functional. As a result, it has become very popular as a fully featured, low
cost alternative to expensive commercial database management systems.
When connecting to MySQL databases, thedatabase nameis the only required parameter on sqlconnect.
The sample programs in this chapter all connected to a MySQL database named test. These two
statements from those sample scripts illustrate the connection in the sqlconnect function, and the
selection of the MySQL test database in the second statement:
if SQLConnect(,,,'mysql') <> 0 then call sqlerr 'On connect'
if SQLCommand(u1,"use test") <> 0 then call sqlerr 'On use'
One way in which MySQL differs from many other database management systems is that only certain
kinds of MySQL tables support transactions. The sqlcommit and sqlrollback functions only
provide transactional control against tables that specifically support transactions. So, you must use the
proper kind of table to write transactional programs. Another difference of which you should be aware
is that MySQL does not support bind variables.
249
Chapter 15
The sqldefault and sqldescribe functions operate slightly differently under various databases. The
sqlvariable and sqlgetinfo functions return slightly different information for different databases.
Finally, the way in which SQL statements themselves are encoded will sometimes vary. This is due to the
databases themselves, not because of the Rexx/SQL interface. While most DBMSs support various ANSI
SQL standards, most also support keywords and features beyond the standards. Oracle is an example.
Oracle SQL is one of the most powerful database languages, but it achieves this power at some cost in
standardization. Be aware of variants from SQL standards if retargeting Rexx/SQL scripts toward differ
ent DBMSs.
One example is IBM’s commercial interfaces for its DB2 Universal Database (DB2 UDB). DB2 UDB runs
on a variety of operating systems including Linux, Unix, Windows, and mainframes. The mainframe
product has a different code base than that sold for Linux, Unix, and Windows. Writing Rexx-to-DB2
scripts on the mainframe is popular because scripting offers an easy way to customize database manage
ment activities. Rexx is an easier language to program with than the alternatives in tailoring and manag
ing the database environment.
This discussion focuses on DB2 UDB for Linux, Unix, and Windows (LUW). We discuss the LUW prod
uct because more readers will likely have access to one of these operating systems than a mainframe
platform. But the Rexx scripting for data manipulation language, or DML, statements we present here
for DB2 UDB under LUW is essentially the same as you would code when using mainframe DB2.
As opposed to a generic database interface like Rexx/SQL, the IBM Rexx / DB2 interfaces give much
greater control over DB2 UDB, including all its administrative functions and utilities. The downside is
that the Rexx/DB2 interfaces are DB2-specific. They are nonportable and come bundled with a pur
chased commercial database. They only operate against DB2 databases, whereas Rexx/SQL operates on
nearly any relational database.
Among IBM’s programming interfaces for managing and controlling DB2 UDB databases, the Rexx/DB2
interfaces are easier to program than the alternatives (those for compiled languages like C/C++,
COBOL, or FORTRAN). They bring the power and productivity of Rexx scripting to the administration
and management of DB2 UDB. Check IBM’s interface documentation to see which Rexx interpreters
their interfaces currently support.
Let’s take a look at the Rexx/DB2 package. Three Rexx/DB2 interfaces come bundled with DB2 UDB for
Linux, Unix, and Windows:
250
Interfacing to Relational Databases
SQLEXEC The SQL interface. Use this to access databases and issue SQL state
ments. This interface supports the kinds of SQL processing illus
trated in this chapter with Rexx/SQL, for example, DML statements,
cursor processing, and parameter markers.
SQLDB2 An interface to DB2’s command-line processor(CLP). Use it to run any
of the hundreds of commands the CLP supports, including those for
attach, connect, backup, restore, utilities, an the like.
SQLDBS An interface to DB2’s Administrative APIs. Use this to script adminis
trative tasks for DB2 databases.
These three interfaces give Rexx scripts complete control over DB2 UDB. Not only can you program
DML and DDL statements, but you can also script database administration, utilities, configuration
changes, and the like. Rexx scripts can even run database stored procedures on most platforms.
The Rexx statements that access the Rexx/DB2 interfaces vary slightly by operating system. Under
Windows, for example, Rexx scripts use the SAA standards to register and load these three DB2 inter
faces. This is the same standard for access to external functions illustrated previously with Rexx/SQL.
For example, these statements set up the three DB2 interfaces for use within a Windows Rexx script:
Once access to the DB2 interfaces has been established, scripts can connect to databases and issue SQL
calls. Here is an example of how to embed SQL statements in scripts using the sqlexec interface. This
code sequence updates one or more rows in a table by issuing the DML update statement:
This example builds a SQL update statement in a variable named statement. It immediately executes
the statement by the sqlexec function. The host variablenamed statement, identified by its preceding
colon (:), contains the SQL statement to execute. The script checks the return code in special variable
sqlca.sqlcode to see whether the SQL statement succeeded or failed. As in the Rexx/SQL interface,
the Rexx/DB2 interface sets a number of variables that pass status information back to the script
through the SQLCA.
251
Chapter 15
In this example, note the use of uppercase for Rexx and SQL statements, and lowercase for literals and
other parts of the code. This is the informal “standard” to which Rexx scripts often adhere in IBM envi
ronments and in mainframe programming. It’s a popular way of coding that serves to identify different
parts of the code. Of course, since Rexx is not case-sensitive, you can use whatever case or mix of case
you feel comfortable with or find most readable. The only exception is the data itself (in character string
literals within Rexx scripts and character data residing in the database). These are case-sensitive.
Here’s another coding example. These statements show how to set up cursor processing using the
SQLEXEC interface:
This time the script builds the SQL statement in the variable named prep_string. The question mark
(?) is a parameter marker or placeholder variable for which values will be substituted.
The SELECT statement is dynamically prepared. The SQLEXEC interface first PREPAREs the SELECT;
then it DECLAREs and OPENs the cursor. After executing the preceding code, a FETCH loop would then
process each row returned in the result set, and a CLOSE statement would end the use of the cursor.
One issue in cursor processing is how to detect null values. Null values are data elements whose values
have not been set. Whether a column can contain nulls depends on the column and table definitions, and
also whether any column values have not been loaded or inserted. To detect null values, the Rexx/DB2
interface uses indicator variables. The keyword INDICATOR denotes them, as in this example:
If the indicator variable cmind is set to a negative value by the interface, then the column variable cm is
null. A null variable indicates that a column entry has not yet been assigned a value in the database.
Calls to the Rexx/DB2 SQLDB2 and SQLDBS interfaces are coded like those we’ve discussed in
illustrating the SQLEXEC preceding interface. Here are the generic templates for invoking the SQLEXEC,
SQLDB2, and SQLDBS interfaces. Each names the interface, then follows it with a SQL statement or
command string representing the function desired in the call:
These three code examples appear in the IBM manual, IBM Db2 UDB Application Development Guide. This
and all other Db2 manuals can be freely accessed or downloaded from the online IBM Publications
Center, as described in Appendix A.
252
Interfacing to Relational Databases
You can see from the code examples in this section that coding the Rexx/DB2 interface is slightly differ
ent from coding SQL calls with the Rexx/SQL package. Nonetheless, if you know one of these two inter
faces, it is quite easy to learn the other. The principles that underlie how to code data manipulation and
data definition statements are the same in both products.
Summary
This chapter overviews of the features of the Rexx/SQL interface in accessing relational databases.
Rexx/SQL is an open-source product that accesses almost any type of SQL database.
The examples showed how quick and convenient Rexx/SQL coding is. It allows single-statement execu
tion of SQL statements, including select’s and DDL. Yet it also supports statement preparation, bind
variables, auto-commits, and all the other features programmers might want in their call-level database
interface.
We discussed five sample scripts that use the Rexx/SQL interface. The first illustrated the basic mecha
nisms of creating and terminating database connections. It also retrieved and displayed database version
and release information. The second script showed how to create and load a database table. Two scripts
showed how to read all the rows of a table. The first used Rexx/SQL’s “array” notation to refer to indi
vidual table rows, while the second illustrated the more standard but cumbersome approach called cur
sor processing. An update script showed how to retrieve and update individual rows within a table. It
also illustrated the value of explicitly committing data from within a script.
We also took a quick look at IBM’s proprietary Rexx/DB2 interfaces. These exemplify the kinds of
database-specific programming and administration possible in Rexx scripts. Scripting these tasks is
much more productive than using traditional compiled programming languages. While we did not walk
through complete sample scripts illustrating the Rexx/DB2 interfaces, we discussed several code snip
pets that show how these interfaces are coded.
This chapter just touches upon the broad topic of database programming. Our purpose is to describe
Rexx database scripting and to demonstrate its coding in a simple manner. If you need more information
about database programming, please obtain one of the many books on that topic.
253
Chapter 15
5. SQL statement syntax is complex. Tell how you can code to quickly identify and reduce syntax
errors.
6. What is the purpose of the sqldispose function? How does it differ from sqldisconnect?
7. Compare the use of Rexx/SQL to the bundled Rexx/DB2 interfaces for scripting with DB2 UDB.
What are the advantages of each toolset?
254
Graphical User Interfaces
Overview
This chapter explores graphical user interface, or GUIpackages. It gives you an overview of the
major packages, explains when to use each, and explores how to design scripts that use them.
GUI development is a detail-oriented process and scripts that create and manage GUIs typically
require many lines of code. We cannot cover all the ins and outs of GUI programming in a single
chapter. GUI programming is a study in its own right. It means learning the many functions,
parameters and attributes involved in windows programming. Our goals here are to describe the
different GUI interfaces available to Rexx programmers and offer guidance on the advantages and
drawbacks of each. We also give you an idea of the structure and design of typical GUI-based
scripts. The sample scripts are quite basic, yet studying them should equip you to move into more
serious GUI scripting.
As a universal scripting language, Rexx runs on every imaginable platform. One advantage of this
versatility is that several GUI packages interface with Rexx. These include Rexx/Tk, Rexx/DW,
Rexx Dialog, ooDialog, RexxGTK, Dr. Dialog, VX*Rexx, and VisPro/REXX. The downside to this
variety is that no single GUI interface has established itself as the de facto standard for Rexx
developers.
In this chapter, we’ll first briefly characterize the major GUIs available for Rexx scripting. For each,
we’ll mention some of its advantages and uses, and we’ll list the environments in which it runs or
is typically used. These brief product profiles orient you to which interface product might be most
appropriate for your own applications. Following these short product profiles, we’ll look at three
packages in greater detail: Rexx/Tk, Rexx/DW, and Rexx/gd. The first two packages aid in script
ing Rexx GUIs, while the latter is for creating graphical images. We’ve selected these three pack
ages for detailed, coding-level coverage for specific reasons. All three are:
Let’s sCuat with the brief sketches of the major GUI ioteafuces.
Chapter 16
Rexx/Tk
Rexx/Tk allows Rexx scripts to use the Tk, or “ToolKit,” GUI popularized by the Tcl/Tk scripting lan
guage. This package enables the development of portable, cross-platform GUIs. Tk supports all impor
tant widgets or window elements. Its dozens of functions categorized as Menus, Labels, Text, Scrollbars,
Listboxes, Buttons, Text Entry, Sliders, Frames, Canvas, Window Design, Event Handlers, and
Convenience functions.
To use Rexx/Tk, you must install both it and the Tcl/Tk scripting language. Your Rexx scripts invoke
Rexx/Tk external functions, which then run their corresponding Tcl/Tk commands. The names and pur
poses of the Rexx/Tk functions are similar to their corresponding Tk commands, so if you know one,
you know the other.
The advantage to Rexx/Tk is that Tk is one of the most widely used cross-platform GUI toolkit in the
world. It runs on all major platforms. Tk became popular because it makes the complex, detail-oriented
process of creating GUIs relatively easy. Sharing Rexx’s goal of ease-of-use makes Tk a nice fit for Rexx
scripting. Those who already know the Tk interface experience little learning curve with Rexx/Tk. You
could read a Tcl/Tk book to learn Rexx/Tk. Plenty of documentation and tutorials are available.
The downside to Rexx/Tk is that it requires Tcl/Tk on your system and has the performance penalty
associated with a two-layer interface. If problems arise, you could find yourself dealing with two levels
of software — the Rexx/Tk interface with its functions, and the corresponding Tcl/Tk commands.
Rexx/Tk is open-source software distributed under the GNU Library General Public License, or GNU
LGPL. Information on Rexx/Tk and downloads are http://rexxtk.sourceforge.net/index.html.
Rexx/DW
This GUI package is based on Dynamic Windows, or DW, a lightweight GUI framework modeled on the
GTK toolkit of Unix and Linux (GTK is also known as GTK+ and the Gimp Toolkit). GTK is open source
under the GNU LGPL license. With Rexx scripts, Rexx/DW presently runs under the Windows, Linux,
Unix, and OS/2-derived environments.
Widgetsare the basic display items placed on GUI windows. The Dynamic Windows package supports
a wide variety of widgets, including: Entryfield or Editbox, Multiline Entryfield or Editbox, Combobox,
Button, Radio Button, Spin Button, Checkbox, Container or Listview, Treeview, Splitbar,
Bitmap/Pixmap/Image, Popup and Pulldown Menus, Notebook, Slider, Percent or Progress meter,
Listbox, Render/Drawing Area, Scrollbar, and Text or Status Bar.
Rexx/DW differs slightly from the Dynamic Windows framework in that it offers a few special functions
beyond what DW contains, while it lacks a few others DW has. So, while Rexx/DW closely follows
Dynamic Windows’ functionality, it is not an exact match.
The main advantage to Rexx/DW is that it is a lightweight interface. Compared to Rexx/Tk, Rexx/DW
provides a cross-platform GUI, while eliminating the overhead of Tcl/Tk that Rexx/Tk requires.
Rexx/DW addresses the performance concerns that sometimes arise when programming GUI interfaces.
Sometimes it’s simpler not to have the Tcl/Tk system installed on the computer and involved as an
intermediate software layer.
256
Graphica I User Interfaces
Rexx/DW is a newer project than Rexx/Tk. Programmers who compare the two may wish to compare
the level of ongoing effort behind the two projects when deciding which to use. An easy way to do this
is to access the Web pages for the respective products at SourceForge.net.
Rexx/DW is open-source software distributed under the GNU LGPL. Information on Rexx/DW and
downloads are at http://rexxdw.sourceforge.net/ . The GTK+ project homepage is located at
www.gtk.org/.
Rexx Dialog
This GUI is specifically designed for Windows operating systems with either the Reginald or Regina
Rexx interpreters. Reginald is a Rexx interpreter based upon Regina, and was extended and enhanced
with Windows-specific functions. It specifically targets Windows platforms. (Reginald is currently out
of support.) Rexx Dialog is the component added to support the typical kinds of GUI interactions users
expect from Windows-based applications. It optimizes the GUI for Windows. Where portability is not a
concern, Rexx Dialog brings Windows “power programming” to Rexx developers.
Chapter 23 covers Reginald and its Windows-oriented features. That chapter provides further informa
tion on Rexx Dialog and its functions, as well as information on where to download the package. It also
offers examples of a few of Reginald’s Windows-oriented functions.
RexxGTK is an ooRexx class library that provides access to the cross-platform GTK graphical interface
available on most mainstream operating systems (eg, Linux, Unix, BSD, Windows, Mac). You must have
the GTK+ libraries installed to use it. This provides an extensive set of free and open source classes and
methods for developing GUI applications. RexxGTK is designed for use with ooRexx and does not work
with other Rexx interpreters.
257
Chapter 16
Rexx/Tk
Now that we’ve profiled the major GUI interface packages, let’s explore three of the most popular in
greater detail. We start with Rexx/Tk, a popular, portable interface modeled on the Tk interface popular
ized by the Tcl/Tk scripting language.
Rexx/Tk is an external function library that provides Rexx scripts interface to the Tcl/Tk command lan
guage. Rexx/Tk has well over 100 functions. Each directly corresponds to a Tcl/Tk GUI command. The
Rexx/Tk documentation maps the Tcl/Tk graphics commands to their equivalent Rexx/Tk functions.
This means that you can learn Rexx/Tk programming from Tcl/Tk graphics books. Or, to put it another
way, to program in Rexx/Tk you must know something about Tcl/Tk GUI programming.
Rexx/Tk includes another 50 plus “extensions,” extra functions that map onto Tcl code that is dynami
cally loaded during Tcl programming. This provides all the GUI facilities Tcl/Tk programmers have
access to, whether or not those functions are dynamically loaded.
The Rexx/Tk function names correspond to their Tcl/Tk equivalents. For example, Tk’s menu command
becomes TkMenu in the Rexx library; menu post becomes TKMenuPost. This makes it easy to follow
the mapping between the Tcl/Tk command and Rexx/Tk functions.
Tcl/Tk is case-sensitive. Quoted commands or widgets must be typed in the proper case. The special
return code tkrc is set by any Rexx/Tk function. tkrc is 0 when a function succeeds, negative on
error, or a positive number for a warning. The TkError function makes available the full text of any
error message.
Appendix G lists all the Rexx/Tk functions and extensions and their definitions. It gives you the com
plete overview of the functions provided with the product.
Under Windows we did nothing more than download the *.zip file, decompress it, and run the
installer program. For first time users of Rexx/Tk, we recommend the “default install” of Tcl/Tk to
avoid any problems.
After installing Tcl/Tk, be sure to run one or more of its “demo” programs. These reside in a subdirec
tory to the product directory and have the extension *.tcl. Running a demo program ensures that
your installation succeeded.
The next step is to download and install the Rexx/Tk package. Rexx/Tk can be freely downloaded from
SourceForge.net. The Rexx/Tk Web page documents the package at http://rexxtk.sourceforge .
net/. The Web page includes a link to download the product, or go to http://sourceforge.net
and enter keywords Rexx/Tk into the search panel.
258
Graphical User Interfaces
The product is available in source and binaries for various platforms. After downloading and decom
pressing the appropriate file, read the readme and the setup.html files that describe product installa
tion. You must set environmental variables and the path to reflect the product and library location. To
use the external function library, your scripts must be able to load the Windows DLL named
rexxtk.dll or the Linux or Unix shared library file named librexxtk*.
Rexx/Tk is an external function library, as is Rexx/DW. Either is usable from any Rexx interpreter that
supports standard access to external functions. Both are always tested with the Regina interpreter, so if
you experience problems that appear to be interpreter-related, verify your install by testing with Regina.
A simple example
Let’s review a very simple sample script. We’ve kept it minimal so that you can see the basic script struc
ture. The goal here is not to explore Tk widgets, of which there is a very full universe. It is simply to ori
ent you to the typical design of Rexx/Tk scripts.
The script was developed under Microsoft’s Windows operating system, but Rexx/Tk’s portability
means it could have been developed for several other operating systems, including Linux and Unix, as
well.
All the sample script does is display a small GUI window with a menu bar. The sole option on the menu
bar in the window is labeled File. When the user clicks on File, a drop-down menu appears. It con
tains three items labeled Open ... , Dir..., and Quit. So the drop-down menu structure is:
File
Open...
Dir...
Quit
If the user selects Open..., the standard Windows panel for File Selection appears. The user selects a file
to “open,” and the script simply confirms the user’s selection by displaying that filename in a Message
Box. The user clicks the Ok button in the Message Box and returns to view the original window.
Similarly, if the user selects Dir..., the standard Windows dialog for Directory Selection appears. After
the user picks a directory, the script displays the directory name in a Message Box to confirm the user’s
selection. The user clicks the Ok button in the Message Box and returns to the original window.
If the user selects Quit, a Message Box asks him or her Are You Sure? with Yes and No buttons below
this question. Selecting the No button takes the user back to the original window and its menu bar.
Clicking Yes makes the window disappear and the script ends.
/*******************************************************************/
/* REXX_TK EXAMPLE: */
/* */
/* A very simple example of the basics of Rexx/TK. */
/*******************************************************************/
260
Graphical User Interfaces
The first line of the script uses the SAA function rxfuncadd to register the function tkloadfuncs,
which will be used to load the Rexx/Tk library:
The key parameter is the second one, rexxtk, which matches the filename for the external library. In
Windows, for example, the file’s name would be rexxtk.dll. Under Linux, Unix, or BSD, the parame
ter identifies the shared library file.
The installation of the Rexx/Tk library ensures that the Rexx interpreter can find this library through the
proper environmental variable. If this line fails in your script, review the install readme* files for how to
set the environmental variables Rexx requires to locate external libraries.
Once the rxfuncadd function has registered the tkloadfuncs function, execute tkloadfuncs to load
the entire external library. Now all the Rexx/Tk functions are available for the use of this script:
This example assumes that we’re using the Regina Rexx interpreter, which bases its access to external
function libraries on the SAA standard. Other Rexx interpreters that follow the SAA interface standards
to external libraries would use the same code as this script. Some Rexx interpreters accomplish access to
external function libraries in a different manner.
The code in the top_window internal subroutine can establish all sorts of widgets (or controls) and
attach them to the topmost window. We’ll look at the code of the subroutine in a moment. The point here
is that the script creates and then displays a window with which the user will interact.
Having displayed its initial window, this code is the basic loop by which the script waits for user interac
tion with the widgets or controls on the top-level window:
The script ends when the user selects the action to end it from the top-level window. The following code
should therefore never be reached, but just in case, always drop the Rexx/Tk functions and code an
exit instruction to end the main routine:
That’s all there is to the main routine. Pretty simple! The real work in most GUI scripts is in the defini
tion of the widgets or controls and the development of the routines that handle the events prompted by
user interaction with those controls.
261
Chapter 16
Here’s the internal subroutine that creates the top-level window and all its widgets:
The first line creates a menu bar for the top-level window. In Tk, the topmost window is denoted by a
period ( .), and all widgets on that window derive their name from this. This line creates the menu bar
we have named .ml for the topmost window:
After creating the menu bar, the script can create a drop-down menu to attach to it. These two lines cre
ate the drop-down menu at the far left side of the menu bar in the main window. The invocation of the
tkadd function attaches the drop-down menu to the menu bar:
With the drop-down menu in place, the script needs to add items to this menu. Three more calls to
tkadd add the three items in the drop-down menu:
A single call to the tkconfig function completes the set up by attaching the menubar to the window:
The routine has completed its task of building the top-level window and its widgets. It ends with a
return instruction.
Now we need to create three routines, one for each of three actions the user can select from the drop
down menu. The tkadd functions above show that the labels the user will view for these three actions
are Open..., Dir..., and Quit. Those lines also show that the corresponding routines we need to cre
ate for the three actions must have the names of getfile, getdirectory, and exit_window. So the
262
Graphical User Interfaces
tkadd function associates the label the user selects with a routine in the script that will be run when he
or she selects the label from the drop-down list.
Here is the code for the getfile routine, the routine that displays the typical Windows panel from
which users select filenames (the Windows’ File Selection panel). The tkmessagebox call displays back
the filename the user selects in a Message Box and allows the user to exit back to the main window by
pressing the Ok button:
if TkMessageBox(‘-message’,filename,’-title’, ,
‘Correct?’,’-type’,’ok’,’-icon’,’warning’) = ‘ok’ then nop
return
The tkgetopenfile function sets up the Window’s File Selection dialog. You can see the power of a
widget or Windows control here: a single line of code presents and manages the entire user interaction
with the File Selection dialog.
The code to implement the directory selection routine is nearly the same as that for the routine above,
except that a Windows-style Directory Selection panel appears instead of a File Selection panel. Once
again, the tkmessagebox call echoes the user’s choice back to him or her inside a Message Box. The user
acknowledges the Message Box and continues interaction with the script by clicking on the message ok
displayed inside that Message Box:
if TkMessageBox(‘-message’,dirname,’-title’, ,
‘Correct?’,’-type’,’ok’,’-icon’,’warning’) = ‘ok’ then nop
return
Lastly, here is the code that executes if the user selects option Quit from the drop down menu. It dis
plays a Message Box that asks Are You Sure? If the user pushes the No button, he or she again sees
the top-level window because of the return instruction in the code below. If he presses the Yes button,
he exits the script and its window. This executes the tkdropfuncs function below, which drops the
Rexx/Tk function library from memory and further use by the program:
263
Chapter 16
This sample script is very minimal. It just displays a small window with a drop-down menu and man
ages user interaction with the window and its menu selections. Nevertheless, the script does illustrate
the basic structure of GUI scripts and how they manage user interaction. You could take this “skeletal
script” and expand it into a much more robust and complex window manager.
Start by perusing the sample scripts shipped with Rexx/Tk. You can learn a lot from them. And consider
learning more about the Tcl/Tk commands that underlie Rexx/Tk. Two good sources of information are
the Tcl/Tk Developer’s home page, listed earlier, and any of several popular books on how to program
the Tcl/Tk GUI. Among those books are Graphical Applications with Tcl and Tkby Eric F. Johnson (M&T
Books, ISBN: 1-55851-569-0) and Tcl/Tk in a Nutshellby Raines and Tranter (O’Reilly, ISBN:
1-56592-433-9). You can find many other books on the Tk toolkit by searching online at www.amazon.com
or www.barnesandnoble.com.
Rexx/DW
Rexx/DW offers an alternative GUI toolkit to that of Rexx/Tk. Rexx/DW’s main advantage is that it is a
lightweight interface, offering potential performance improvements over Rexx/Tk.
Rexx/DW provides external functions that enable Rexx scripts to create and manage GUIs through
Netlabs.org’s Dynamic Windows, or dwindows, package. Rexx/DW scripts define widgets, elements placed
in windows, such as check boxes, radio buttons, and the like. Widgets are assembled into the window
layout by a process called packing. Internal subroutines you write called event handlersor callbacksare
associated with particular actions the user takes on the widgets.
Scripts typically present a window or screen panel to the user and wait for the user to initiate actions on
the widgets that activate the callback routines. Interaction continues as long as the user selects an action
from the window. At a certain point, the user closes the window. This ends interaction and terminates
the program.
Components
To set various layout and behavioral attributes, Rexx/DW has about 30 constants. Each constant has a
default and can be set by the script to some other value to change behavior.
Rexx/DW contains over 175 functions. These categorize into these self-descriptive groupings:
□ ProccosControl
□ Dialog
□ CallbackMasagemest
264
Graphical User Interfaces
□ Browsing
□ CorsurPupport
□ModuleSupport
□MotexSorrCrt
□EventSorrCrt
□ThreadSorrCrt
□PCinterPCsitiCn
□Utility
□PackageManagement
Download either compressed source or binaries for your operating system. The installation follows the
typical pattern for open-source software. If you downloaded binaries, after decompression all you must
do is set environmental variables and the path to reflect the product and library location. To use the
external function library, your scripts must be able to load the Windows DLL named rexxdw.dll or the
Unix/Linux/BSD shared library file named something similar to librexxdw*. The readme* file that
downloads with the product gives installation instructions and details on how to set environmental
variables.
1. Register and load the Rexx/DW external function library. Use code such as this:
call RxFuncAdd ‘DW_LoadFuncs’, ‘rexxdw’, ‘DW_LoadFuncs’
if DW_LoadFuncs() <> 0 then say ‘ERROR-- Unable to load Rexx/DW library!’
The first line uses the SAA-based function in Regina Rexx named rxfuncadd to register the
dw_loadfuncs external function. It resides in the external library named named by the second
parameter, rexxdw .
265
Chapter 16
In Windows, rexxdw refers to the file rexxdw.dll. In Linux or Unix, it refers to the root part
of the name of the shared library file. In either case, the proper environmental variable must be
set to indicate the location of this file for the rxfuncadd call to succeed. The second line
invokes the dw_loadfuncs function to load the rest of the DW external library.
2. Initialize the dynamic windows interface by invoking the Rexx/DW dw_init function.
Initialize various attributes in the constants to set interface behaviors and defaults.
3. Create the topmost panel or window. This screen may consist of a set of packed widgets, each
having various attributes and behaviors. Events are mapped into callbacks or event-handling
routines for the various actions the user might take on the window, based on the widgets it
contains. This mapping is achieved through the function dw_signal_connect and potentially
other callbackmanagement functions. When all is ready, the script displays the top-level win
dow to the user.
Now the script driver runs an endless loop that receives actions from the user. Depending on
the capabilities of the Rexx interpreter, this loop might use either of the functions dw_main
or dw_main_iteration. This loop is similar to that of the tkwait function loop in Rexx/Tk.
4. The user ends interaction with the script by closing its top-level window.
In summary, you can see that the skeletal logic of Rexx/DW programs is the same as the sample
Rexx/Tk script we discussed earlier in the chapter. So, scripting Rexx/DW interfaces is rather similar to
scripting Rexx/Tk. The difference is that you use Rexx/DW functions to bring the logic to life. The real
work in Rexx/DW scripting is in writing the callback routines to handle user interaction with the wid
gets on the window.
Fortunately, Rexx/DW comes with complete documentation and sample scripts. Use these as models
with which to get started. Take the sample scripts, look them over until you understand them, then copy
them and adapt them to your own needs. This will get you up and running quickly.
266
Graphical User I nterfaces
Rexx/gd draws complete graphic images with lines, arcs, text, color, and fonts. Images may be cut and
pasted from other images. The images that are created are written to PNG, GIF, JPEG, or JPG files.
Rexx/gd is based on GD, an open-source, ANSI C-language library. Rexx/gd is essentially a wrapper
that gives Rexx scripts access to the GD library code. To use Rexx/gd, you need to download and install
the GD library to your machine. Then download and install Rexx/gd.
The GD library is available at libgd.github.io. Or enter the keywords gd library into any
Internet search engine for a list of current download sites. Rexx/gd can be downloaded off the same
master panel as Rexx/SQL and Rexx/DW at http://regina-rexx.sourceforge.net/ or more
specifically http://rexxgd.sourceforge.net/index.html.
1. Register and load the Rexx/gd library for use. Following the same style we used with Rexx/Tk
and Rexx/DW, this code looks like this:
call RxFuncAdd 'GdLoadFuncs', 'rexxgd', 'GdLoadFuncs'
if GdLoadFuncs() <> 0 then say 'ERROR-- Unable to load Rexx/gd library!'
This code registers and loads the GD function library for use according to the standard
approach of the SAA registration procedures for external function libraries.
2. Allocate a work area to develop an image in by invoking the gdImageCreate function.
3. Assign background and foreground colors to the image by calling the gdImageColorAllocate
function.
4. Use one or more of the drawing functions to draw graphics in the image area. For example, to
draw a line, call gdImageLine. To create a rectangle, invoke gdImageRectangle or
gdImageFilledRectangle. The script might also invoke styling, brushing, tiling, filling, font,
text, and color functions in creating the image.
5. The script preserves the image it created in-memory by writing it to disk. Among useful
externalization functions are gdImageJpeg, to write the image as a JPEG file, and
gmImagePng, to store the image as a PNG file.
6. End by releasing memory and destroying the in-memory image by a call to gdImageDestroy.
267
Chapter 16
Figure 16-2
Rexx/gd provides over 75 functions. They are divided into these categories:
Rexx/yd cnn be combined with GUI tools like Rexx/Tk or Rexx/DW to create graphical user interfaced.
It is also useful in buildiny parts of Web payed. In fact, let’s look nt n sample script that does exactly that.
A sample program
This sample script draws the buttons that appear on a Web paye. Each button contains one word of text.
Fiyure 16-3 displays the Web paye, which is the home paye for Rexx/yd at SourceForye.net at http://
rexxgd.sourceforge.net/index.html . The buttons created by the proyram appear down the left
hand side of the Web paye. The script appears courtesy of its author, Mark Hessliny, developer/main-
tainer of Reyina Rexx as well as many other key open-source Rexx tools.
268
Graphical User Interfaces
Figure 16-3
Here is the program. (A few lines in the program wrap around onto the next line due to the margin size.)
/*
* This Rexx/gd script creates all of the buttons for my Web page
* /
Trace o
Call RxFuncAdd ‘GdLoadFuncs’, ‘rexxgd’, ‘GdLoadFuncs’
Call GdLoadFuncs
text = ‘Home Links Downloads Bug_Report Rexx/Tk Rexx/SQL Regina THE PDCurses
Rexx/Wrapper Documentation Rexx/ISAM Rexx/gd Rexx/Trans Rexx/Curses’
/*
* Find the maximum length of any of the button texts
* /
maxlen = 0
Do i = 1 To Words(text)
if Length(Word(text,i)) > maxlen Then maxlen = Length(Word(text,i))
End
/*
* Image size is based on size of largest text
* /
font = ‘GDFONTMEDIUMBOLD’
x = ((1+GdFontGetWidth( font )) * maxlen) + 8
y = GdFontGetHeight( font ) + 8
Say ‘Image size:’ x ‘x’ y
Do i = 1 To Words(text)
img = GdImageCreate( x, y )
269
Chapter 16
/*
* First color allocated is the background - white
* /
white = GdImageColorAllocate( img, 245, 255, 250 )
background = GdImageColorAllocate( img, 0, 0, 102 )
blue = GdImageColorAllocate( img, 0, 0, 255 )
yellowgreen = GdImageColorAllocate( img, 73, 155, 0 )
/*
* Although most browsers can’t handle transparent PNGs,
* set the transparent index to the background anyway.
*/
call GdImageColorTransparent img, background
/*
* Determine text position - centered
* /
xoff = (GdImageGetWidth( img ) % 2 ) - (((Length(Word(text,i)) *
(GdFontGetWidth( font )))-1) % 2)
/*
* Draw our borders for the fill of the top left and right corners.
* /
call GdImageLine img, 6, 0, 0, y-1, background
call GdImageLine img, x-7, 0, x-1, y-1, background
call GdImageFillToBorder img, 0,0, background, background
call GdImageFillToBorder img, x-1,0, background, background
/*
* Write the string in blue, and save the image . . .
* /
call GdImageString img, font, xoff, 3, Translate(Word(text,i), ’ ‘,’_’),
yellowgreen
call GdImagePNG img, makename(Word(text,i),’green’)
/*
* . . . then overwrite the string in yellow-green, and write this image.
* /
call GdImageString img, font, xoff, 3, Translate(Word(text,i), ’ ‘,’_’), blue
call GdImagePNG img, makename(Word(text,i),’blue’)
Return
makename: Procedure
Parse Arg text, color
text = Translate(text,’abcdefghijklmnopqrstuvwxyz’,’ABCDEFGHIJKLMNOPQRSTUVWXYZ ’)
text = Changestr( ‘/’, text, ‘’ )
text = Changestr( ‘_’, text, ‘’ )
Return color||text’.png’
The logic of the script follows the straightforward steps listed in the preceding code. First, the script
loads the gd function library:
270
Graphical User Interfaces
The script next determines the size of the buttons, based on the size of the longest word that will be dis
played within them. This is the code of the do loop and some code that calculates the image size.
Now the script is ready to invoke the Rexx/gd function gdimagecreate to allocate the image. The
image will be developed in a work area in memory:
img = GdImageCreate( x, y )
The script issues several gdimagecolorallocate functions to set up colors for the image and its back
ground:
/*
* First color allocated is the background - white
*/
white = GdImageColorAllocate( img, 245, 255, 250 )
background = GdImageColorAllocate( img, 0, 0, 102 )
blue = GdImageColorAllocate( img, 0, 0, 255 )
yellowgreen = GdImageColorAllocate( img, 73, 155, 0 )
Now, the script draws the borders of the buttons with this code:
These statements write the image in blue and yellow-green, and save it to PNG files:
/*
* Write the string in blue, and save the image . . .
* /
call GdImageString img, font, xoff, 3, Translate(Word(text,i), ’ ‘,’_’),yellowgreen
call GdImagePNG img, makename(Word(text,i),’green’)
/*
* . . . then overwrite the string in yellowgreen, and write this image.
* /
call GdImageString img, font, xoff, 3, Translate(Word(text,i), ’ ‘,’_’), blue
call GdImagePNG img, makename(Word(text,i),’blue’)
Now that the image has been allocated, developed, and saved to a file, the script can exit. Before termi
nating, the program destroys the allocated image and releases its memory with this statement:
This script illustrates the straightforward logic of most Rexx/gd programs. As with Rexx/Tk and
Rexx/DW, this logic is simple; the trick is in learning the details of the many available functions and
how to combine them to meet your needs.
The graphical images created with Rexx/gd can be used for a variety of purposes. As shown by this pro
gram, the images can be combined with other logic to create sophisticated Web page designs.
271
Chapter 16
Summary
This chapter describes the most popular GUI interface packages for Rexx scripting. It discusses
Rexx/Tk, Rexx/DW, and Rexx/gd in detail. These are all open-source products that are widely used
and well proven. New releases are tested with Regina and the products work with other Rexx inter
preters as well.
We explored the basics of GUI programming at a very high level, showing the essential nature of event-
driven programming. We presented a Rexx/Tk script, albeit a very simple one. Then we looked at
Rexx/DW scripting. These scripts follow the same basic event-driven logic as the Rexx/Tk program, but
of course use the functions of the Rexx/DW library.
GUI programming is necessarily detail oriented, and scripts tend to be lengthy, even if they are logically
rather straightforward. If you are not an experienced GUI developer, this is the challenge you face. Rexx
provides all the requisite tools.
Finally, we investigated Rexx/gd and how it can be used for creating graphic images. We looked at the
Web page for the product and related the graphics on that Web page to the script that created them.
Rexx/gd is a generic graphical image tool that can be combined with other Rexx interfaces and tools to
create the graphical components of Web pages or for many other uses.
272
17
Web Programming with
CGI and Apache
Overview
Rexx is well-suited to Web programming because it excels at string manipulation. Web program
ming requires reading and interpreting string input and creating textual output. As in the next
chapter on XML, the emphasis is on string processing. Rexx string processing strengths recom
mend it as a highly productive, easy-to-maintain language for Web programming.
There are many ways to program Web servers and build Web pages with Rexx. Two popular
technologies are theCommon Gateway Interface, orCGI, and Apache’sMod_Rexx interface.
First, we describe some of the tools available for CGI programming. CGI was one of the first popu
lar Web server interfaces because it is easy to use and fully programmable.
Then we describe scripting Apache through its Rexx interface, Mod_Rexx. Apache is the world’s
most popular Web server. Mod_Rexx gives you complete scripting control over Apache. With it
you can efficiently and effectively serve Web pages created by Rexx scripts. You can also dynami
cally create Web pages through a feature called Rexx Server Pages, or RSP. Dynamic Web pages are
created and tailored in real time to meet user needs.
To give you an idea of what this library contains, here is a quick list of its functions. The package itself
includes both the technical descriptions and full Rexx source code for these functions.
Function Use
cleanquery Removes unassigned variables from CGI query string
cgierror
Reports the error message and returns
cgidie
Reports the error message and “dies” or exits
chkpwd
Verifies username and password
delquery
Removes an item from CGI query string
deweb
Converts ASCII hex code to ASCII characters
formatdate
Converts date expression to Oracle format
fullurl
Returns complete CGI query URL
getowner
Returns a file’s owner
getfullhost
Returns fully qualified domain name of the local host
htmlbreak
Breaks a long line into lines for HTML parsing
htmlbot
Inserts standard information (“boiler plate”) at page end
htmltop
Inserts title and header at page top
httab
Converts tab-delimited file into HTML table
methget
Returns true if the Form uses method=”get”
methpost
Returns true if the Form uses method=”post”
myurl
Adds the script’s URL to the page
oraenv
Establishes SLAC’s Oracle/Rexx environment
printheader
Inserts the Content-type header
printvariables
Adds the Form’s name-value variable pairs to the page
readform
Reads a Form’s get or post input and returns it decoded
readpost
Reads a Form’s standard input with method=”post”
274
Web Programming with CGI and Apache
Function Use
The cgi-lib.rxx package comes with several sample scripts. Here’s a simple one that illustrates sev
eral of the functions. It simply reads form input from the user and echoes it to a Web page. It appears
here courtesy of its author Les Cottrell and the SLAC:
#!/usr/local/bin/rxx
/* Minimalist http form and script */
F=PUTENV(“REXXPATH=/afs/slac/www/slac/www/tool/cgi-rexx ”)
SAY PrintHeader(); SAY ‘<body bgcolor=”FFFFFF”>’
Input=ReadForm()
IF Input=’’ THEN DO /*Part 1*/
SAY HTMLTop(‘Minimal Form’)
SAY ‘<form><input type=”submit”>’,
‘<br>Data: <input name=”myfield”>’
END
ELSE DO /*Part 2*/
SAY HTMLTop(‘Output from Minimal Form’)
SAY PrintVariables(Input)
END
SAY HTMLBot()
F=PUTENV(“REXXPATH=/afs/slac/www/slac/www/tool/cgi-rexx ”)
The line is coded for uni-REXX, a commercial Rexx interpreter from The Workstation Group (see
Chapter 19 for information on uni-Rexx and other major commercial Rexx interpreters). Your statement
for library access would be coded differently if you use a different Rexx interpreter. For example, using
Regina and most other interpreters you could code this statement with the value built-in function. The
first parameter in the statement below is the symbol to change, the second is the value to set it to, and
the third is the variable pool in which to make the change. The result is to update the environmental
variable properly for access to the function library:
The cgi-lib.rxx package provides full source code for the functions, so you can set them up however
you need to as an external library for your version of Rexx. Or, use them as internal routines.
275
Chapter 17
Next, the script writes the Content Type headerby the printheader function. The content type header
must be the first statement written to the browser. It tells the browser the kind of data it will receive in
subsequent statements:
The next line reads the input form with the readform function:
Input=ReadForm()
If there is no input, the script writes a minimal HTML page using the htmltop function. The htmltop
function inserts a title and header at the top of a Web page:
If there was form input, the script echoes it back to the user by the printvariables function. The
printvariables function adds the form’s name-value variable pairs to the Web page:
The program ends by writing a standard footer to the Web page with the htmlbot function:
SAY HTMLBot()
You can see that developing Rexx scripts that interface to CGI is just a matter of following CGI rules
regarding how input is read into the script and written to the interface. CGI scripts typically read user
forms input, perform some processing, and write textual output that defines the Web page the user sees in
response. A library of functions like those provided by the cgi-lib.rxx package makes the whole pro
cess easier. They offer convenience and higher productivity than manually coding everything yourself.
In concluding, we mention that this Rexx/CGI function library is also the basis for the CGI interface
package offered with the Reginald Rexx interpreter. See Chapter 23 further information on Reginald and
for example Reginald scripts.
276
Web Programming with CGI and Apache
The Inheeneh/REXX HHNS WoekBench consists about 40 functions. This function library comes with
l,lto l hlzee slfsae selhelfs. Eehaish hlctfeeoloile leh ohe ai,elet lee lvlial,ae lo ohe Heeei
Henlult & Sons Web site lt www.hhns.fr/fr/real_cri.html.
The libelet euns unhee Winhows, LinuR, lnh IBM’s AIX oseeltinh ststefs. It is testeh with the Rehinl,
IBM Object REXX, lnh NetReRR inteeseetees. It sussoets two Web seevees: Miceosoft’s Inteenet
Infoefltion Seevices (oe IIS) lnh the Aslche osen-souece Web seevee.
To hive ln ihel of whlt the libelet contlins, heee lee its functions lnh theie uses (lll functions lee
hesceibeh in full hetlil in the seohuct hocufentltion):
Function Use
277
Chapter 17
Function Use
Beyond the Web programming functions, the package includes other useful functions. The following
table shows that they are divided into three categories: mathematical functions, CMS-like functions, and
date functions:
Mathematical These functions support atan, atan2, cos, sqrt, exp, fact,
advanced or transcendental log, pow, sin
mathematics.
CMS These functions support stm2file, stm2var, file2stm,
conversion between filenames var2stm, makefid
and variables and stems.
Date These functions convert between d2date, date2d
Julian day numbers and dates
Let’s take a look at a sample program using this package. This script writes a Web page that lists pro
gram names and their descriptions. The programs it lists are the sample scripts that come with the
Internet/REXX HHNS WorkBench. The output of this program can be viewed at the product Web site,
www.hhns.fr/fr/real_cri.html , and is also depicted in Figure 17-1. The script appears here courtesy
of Henri Henault & Sons.
278
Web Programming with CGI and Apache
Figure 17-1
#! /usr/local/bin/regina
/* A more elaborate list of the samples */
/*-------------------------------------------------------------------- */
say “<center>”
say “<table border=1>”
say tblHdr(“Description”, “ URI”)
do queued()
279
Chapter 17
parse pull z
/*--- assume that 2nd line of the program is its brief description --*/
call linein z; desc = linein(z); call lineout z
parse var desc ‘/*’ desc ‘*/’
/*--- Now, write a table Row with the description and the Web link ---*/
say tblRow(strip(desc), cgiHref(z, z))
end
say ‘</table>’
say ‘</center>’
call cgiEnd
return 0
The program starts by accessing the shared function library by its first statement:
Then it initializes by invoking the cgiinit function to write the page title:
The next several lines get the list of program names (filenames) to place into the list of programs in the
table on the Web page. This code first determines whether the operating system is Windows or a version
of Unix; then it uses the popen function to issue either a dir or ls command to get the directory listing
into the stack. This is a good example of how scripts can be written to operate across platforms through
OS-aware programming:
Now, the program writes the table header, using the tdlHdr function:
say “<center>”
say “<table border=1>”
say tblHdr(“Description”, “ URI”)
280
Web Programming with CG I and Apache
Next, the program executes a do loop to read each program name from the stack. For each one it
retrieves, it uses the tblRow function to write a row into the tabular listing. Each line in the output list
ing contains the program name, followed by the URL hyperlink to its code. The link is produced by the
cgiHref function:
/*--- Now, write a table Row with the description and the Web link -- */
say tblRow(strip(desc), cgiHref(z, z))
After it has created the table of program names and hyperlinks to their corresponding scripts, the pro
gram closes the table:
say '</table>'
say '</center>'
The program concludes by writing a message with a URL link by the cgiHref function. Then it termi
nates by invoking cgiEnd:
call cgiEnd
The Internet/REXX HHNS WorkBench makes CGI programming easier because you can leverage its set
of Web-programming-specific functions for higher productivity. The scripting example employs only a
small number of the package’s functions, yet you can see how these functions make for a higher-level,
more powerful script.
There are many more coding examples at the HHNS Web page at www.hhns.fr/fr/real cri.html.
You can run the examples at the Web site and view their Web page output while viewing the code
simultaneously in another browser panel. This makes it very easy to learn how to use this package.
The Apache Web server directly executes your Rexx scripts through its Mod_Rexx interface. Apache
offers a more efficient way of writing Web server code than the Common Gateway Interface. Web
server extensions like CGI typically suffer from performance overhead because they spawn separate
processes to handle new requests. The Apache server handles new requests by executing within a new
thread, rather than spawning a new process. Threads are a more efficient mechanism than processes on
most operating systems. This also means that Mod_Rexx requires a thread-safe interpreter. Examples of
thread-safe Rexx interpreters include Regina and Open Object Rexx.
281
Chapter 17
Mod_Rexx gives Rexx developers full control over all aspects of the processing of Apache server
requests. The product comes in two flavors. One is a traditional, function-based interface, while the
other is an object-oriented interface. The procedural interface contains roughly 50 functions, all of which
start with the letters WWW. The object-oriented interface consists of three classes and their accompanying
40-odd methods. This chapter focuses on the function-based interface.
□ General Functions — This set of functions provides a base level of services necessary to work
with the Apache Web server. They manage cookies and the error log, retrieve
environmental information, and handle URLs.
□ Apache Request Record Functions — These functions provide information about and manage
the request record pointer, information coming into the script from Apache and the Web.
□ Updatable Apache Request Record Functions — These functions manage the request record
pointer and allow updating values as well as retrieving them.
□ Apache Server Record Functions — These functions manage server-side concerns pertaining
to Apache and its environment.
Appendix J lists all the functions in the Mod_Rexx package along with descriptions of their use.
Mod_Rexx uses a set of three dozen special variables to communicate information to Rexx scripts. The
names of these variables all begin with the letters WWW. These special variables are set either before the
script starts, or after the script executes a function call. Their purpose is to communicate information to
the script either about the environment or the results of function calls. The sample program we discuss
later creates a Web page and displays the values of these variables. Appendix J contains a complete list
of all the Mod_Rexx special variables.
Installation
Mod_Rexx is distributed with Apache. Download Apache from www.apache.org. Or, obtain
Mod_Rexx by separate download from SourceForge at
http://sourceforge.net/projects/modrexx.
Mod_Rexx is distributed under the Common Public License. The license agreement downloads with the
product. Be sure to read it and agree to its terms before using the product. Mod_Rexx runs under
Windows, Linux, and BSD. It is tested with the Regina and Open Object Rexx interpreters.
Installing Mod_Rexx is similar to installing the Rexx interfaces described in the last few chapters. Be sure
that the Mod_Rexx shared library named mod_rexx.dll or mod_rexx.so is present and that it can be
located through the PATH or the proper shared-library environmental variable. The installation instruc
tions explain this in detail.
282
Web Programming with CGI and Apache
One additional step is required: configuring the Apache Web server to execute your Rexx scripts. To
configure Apache, just edit its configuration file and restart the server for the changes to take effect.
Apache’s configuration file is typically named http.conf and is located in the Apache conf (configura
tion) directory. You must add lines to this file that:
Tae lines you add uo uae Apaaae aoniigurauion iile saould look similar uo uaese:
# The following lines should be added at the end of the http.conf file.
#
AddType application/x-httpd-rexx-script .rex .rexx
AddType application/x-httpd-rexx-rsp .rsp
After reconfiguring tais file, saut down and restart tae Apacae Web server so taat tae new directives
uake eiieau.
To test tae install, start your browser and enter tais line into its “address entry boX:”
http://your.domain.com/test.rex
oeplace tae teXt your.domain.com wita tae name of your own server. Tais test runs a oeXX test script
under hod_oeXX and displays a simple HyperteXt harkup Language (HThL) page.
Saould you aave any difficulty, hod_oeXX comes wita documentation taat covers bota installation and
tae relevant Apacae directives. Tae documentation also gives complete information on tae hod_oeXX
function library, tae alternative object-oriented interface, special oeXX variables, and aow to use oeXX
Server Pages.
A sample script
Let’s discuss aow to write scripts taat manage Apacae tarouga tae hod_oeXX interface. First, we’ll
describe tae kinds of processing taese scripts can perform; taen we’ll look at an sample program. Tae
sample script reads input from a user of tae Web server, and writes a Web page to ais or aer browser in
response. It is a typical program in taat it serves Web pages.
283
Chapter 17
You can write scripts that take control from Apache at any point in its request processing. These are the
processing phases during which your script might run:
1. Request
2. Post-read request
3. URI translation
4. Header parser
5. Access control
6. Authentication
7. Authorization
8. MIME type check
9. Fixup
10. Response
11. Logging
12. Cleanup
13. Wait
14. Post-read request
Most scripts are response handlers—they run during the Response phase of Step 10. Response handlers
receive the user’s input and write a Web page to his or her browser in response.
This scripting example is a response handler. The script creates a Web page that displays the value of the
Mod_Rexx special variables. Appendix J lists the Mod_Rexx special variables. Each has a name that
starts with the letters www. This script is one of the sample scripts distributed with the Mod_Rexx pack
age. Here is the script:
284
Web Programming with CGI and Apache
say "<p>The following is the list of standard Rexx CGI variables and their values:”
say ‘<table border=”1”><tr><th>Name</th><th>Value</th></tr>’
say "<tr><td>WWWAUTH_TYPE</td><td>”vorb(wwwauth_type)”</td></tr>”
say "<tr><td>WWWCONTENT_LENGTH</td><td>”vorb(wwwcontent_length)”</td></tr>”
say "<tr><td>WWWCONTENT_TYPE</td><td>”vorb(wwwcontent_type)”</td></tr>”
say "<tr><td>WWWGATEWAY_INTERFACE</td><td>”vorb(wwwgateway_interface)”</td></tr>”
say "<tr><td>WWWHTTP_USER_ACCEPT</td><td>”vorb(wwwhttp_user_accept)”</td></tr>”
say "<tr><td>WWWHTTP_USER_AGENT</td><td>”vorb(wwwhttp_user_agent)”</td></tr>”
say "<tr><td>WWWPATH_INFO</td><td>”vorb(wwwpath_info)”</td></tr>”
say "<tr><td>WWWPATH_TRANSLATED</td><td>”vorb(wwwpath_translated)”</td></tr>”
say "<tr><td>WWWQUERY_STRING</td><td>”vorb(wwwquery_string)”</td></tr>”
say "<tr><td>WWWREMOTE_ADDR</td><td>”vorb(wwwremote_addr)”</td></tr>”
say "<tr><td>WWWREMOTE_HOST</td><td>”vorb(wwwremote_host)”</td></tr>”
say "<tr><td>WWWREMOTE_IDENT</td><td>”vorb(wwwremote_ident)”</td></tr>”
say "<tr><td>WWWREMOTE_USER</td><td>”vorb(wwwremote_user)”</td></tr>”
say "<tr><td>WWWREQUEST_METHOD</td><td>”vorb(wwwrequest_me thod)”</td></tr>”
say "<tr><td>WWWSCRIPT_NAME</td><td>”vorb(wwwscript_name)”</td></tr>”
say "<tr><td>WWWSERVER_NAME</td><td>”vorb(wwwserver_name)”</td></tr>”
say "<tr><td>WWWSERVER_PORT</td><td>”vorb(wwwserver_port)”</td></tr>”
say "<tr><td>WWWSERVER_PROTOCOL</td><td>”vorb(wwwserver_protocol)”</td></tr>”
say "<tr><td>WWWSERVER_SOFTWARE</td><td>”vorb(wwwserver_software)”</td></tr>”
say "</table>”
say "<p>The following are some additional variables provided to the Rexx program:”
say ‘<table border=”1”><tr><th>Name</th><th>Value</th></tr>’
say "<tr><td>WWWDEFAULT_TYPE</td><td>”vorb(wwwdefault_type)”</td></tr>”
say "<tr><td>WWWFILENAME</td><td>”vorb(wwwfilename)”</td></tr>”
say "<tr><td>WWWFNAMETEMPLATE</td><td>”vorb(wwwfnametemplate)”</td></tr>”
say "<tr><td>WWWIS_MAIN_REQUEST</td><td>”vorb(wwwis_main_request)”</td></tr>”
say "<tr><td>WWWRSPCOMPILER</td><td>”vorb(wwwrspcompiler)”</td></tr>”
say "<tr><td>WWWSERVER_ROOT</td><td>”vorb(wwwserver_root)”</td></tr>”
say "<tr><td>WWWUNPARSEDURI</td><td>”vorb(wwwunparseduri)”</td></tr>”
say "<tr><td>WWWURI</td><td>”vorb(wwwuri)”</td></tr>”
say "</table>”
say "</body>”
say "</html>”
return OK
The first few lines in the script define two of the standard Apache return codes:
285
Chapter 17
The next line gets the main argument Apache always passes to every Rexx script. It contains a request
record pointerused as an argument in the coding of subsequent Mod_Rexx functions:
Using this pointer, we can assign a content typeto the responses that will be sent to the browser. This tells
the browser how to interpret the Web page information it will receive next:
Then this required statement gets the query string arguments from Apache:
call WWWGetArgs r
Now, the program can start creating its response to the user. This means writing HTML text to the
browser. All the Rexx script really has to do is issue say instructions to send appropriate text strings. It
begins by writing the page header information:
The next line invokes the wwwgetversion function to get the Mod_Rexx version under which the script
is running. The script displays this information on the Web page:
Now, the program issues a long series of say instructions. We won’t repeat them all here, but here are
the first few say statements:
say “<p>The following is the list of standard Rexx CGI variables and their values:”
say ‘<table border=”1”><tr><th>Name</th><th>Value</th></tr>’
say “<tr><td>WWWAUTH_TYPE</td><td>”vorb(wwwauth_type) ”</td></tr>”
say “<tr><td>WWWCONTENT_LENGTH</td><td>”vorb(wwwcontent_length)”</td></tr>”
Each say instruction displays the value of a different Mod_Rexx special variable on the Web page. These
variables all begin with the letters www.
The program ends by closing the HTML tags and sending a return code string of ok to the caller:
say “</table>”
say “</body>”
say “</html>”
return OK
286
Web Programming with CGI and Apache
The code for a short internal function called vorb ends the program. This internal function is used in
some of the say instructions to space output in a more attractive way.
That’s all there is to it. Knowing just a few Mod_Rexx functions, you can take control of Apache to better
customize Web pages and generate tailored output. This sample script shows how easy it is to get
started. As your knowledge grows, the Mod_Rexx interface gives you the functions required to gain full
scripting power over the Apache Web server.
To set up RSPs, you must configure Apache properly by giving it the appropriate directives and
rebooting Apache. The install instructions above included the lines necessary to configure Apache to
enable RSPs.
Just like server pages coded in other scripting languages, place your Rexx code directly within the
HTML, and frame it between special markers. The delimiters must occur on their own line (without any
other code). They identify the start and end points of Rexx code within the HTML. There are two kinds
of delimiters: short and long. Use either within the HTML to identify your Rexx code:
Here is a sample RSP coded with short delimitersthat show where the Rexx code starts and ends:
You can see that the Rexx code is embedded between the markers <?rexx and ?>. This embedded code
is simply standard Rexx. It enables programmability within the HTML code. The delimiter markers
serve to identify the Rexx code and isolate it as opposed to native HTML code.
287
Chapter 17
As in the example using short delimiters, this example shows how you can embed Rexx code directly
within your HTML code that defines Web pages.
When RSP-enabled code is referenced, Mod_Rexx takes these steps to run it:
Rexx Server Pages allow you to capitalize on your knowledge of Rexx in creating dynamic server pages.
They present an alternative to coding in languages like Java Server Pages or PHP.
Further Information
For further information, visit the Mod_Rexx project pages hosted by SourceForge at http://source
forge.net/projects/modrexx. The website www.RexxInfo.org has tutorials and background
information on both Rexx with CGI and Mod_Rexx.
Summary
Two popular ways to script Web servers are CGI and the Mod_Rexx interface into Apache. This chapter
describes both. Rexx scripts can manage almost any aspect of these popular Web server products, but
most scripts run in response to a user request. These scripts serve Web pages to users.
We looked at three tools: the CGI/Rexx library from Stanford Linear Accelerator Laboratory, the
Internet/REXX HHNS WorkBench from Henri Henault & Sons, and Apache’s Mod_Rexx interface. The
sample script for the first package read a user’s form input and simply echoed it back to a Web page. It
illustrates all the basics of CGI programming, and showed how the functions of the CGI/Rexx library
simplify Web serving. The sample script for the second function library writes a Web page that includes
a list of programs and their descriptions. It illustrates a little more advanced CGI programming, this
time based on the package from Henri Henault & Sons. The final programming example uses Apache’s
Mod_Rexx interface. It serves a Web page that lists all of the Mod_Rexx package’s special variables.
The sample programs were very brief and are intended to show how to get set up and started with these
tools. For further information and complete documentation, visit the Web sites listed during the discus
sions where these tools can be freely obtained.
288
Web Programming with CGI and Apache
289
XML and Other Interfaces
Overview
There are more free and open-source Rexx interfaces and tools than one book can possibly cover,
so this book just introduces a few of the most popular. This chapter describes XML programming
with a package called RexxXML. It tells how to write scripts that manipulate XML and HTML data
files and how to accomplish other XML-related tasks.
To start, we’ll define what XML is, and what related terms like XSLT, XPath, and HTML mean.
Then we’ll look at the kinds of processing you can accomplish using the RexxXML package. After
that, we’ll briefly discuss the functions in RexxXML, including those for document tree parsing,
document tree searching, XSLT processing, and schema validation. After we discuss how to
download and install RexxXML, we’ll illustrate specific XML operations, such as how to load and
process XML documents, how to validate documents, and how to process them against an XSLT
stylesheet. With this background, we’ll review a script that uses RexxXML to read a Web page,
identify a specific data element within that Web page, and compare the value of the data element
to the version of RexxXML used within the script. The script uses the Web page to determine if a
more recent version of RexxXML is available than the user has installed.
The chapter concludes by mentioning many other free and open-source Rexx tools, packages, and
interfaces. Most can be found on the Web and freely downloaded just by entering their name into
any popular search engine. Or visit www.RexxInfo.org for a complete list and free downloads.
XML and its related technologies have become popular as a way to provide self-descriptive, self-validating
data. They underlie the construction of the internet and data transfer between many organizations.
RexxXMLis an external function library that supports common XML operations. Rexx scripts use it to
parse, transform, analyze and generate XML files. RexxXML goes well beyond XML itself to support
HTML, XML dialects, XPath, and XSLT.
RexxXML is built on top of libxml and libxslt. These two free products are part of the XML C parser
and toolkit of the Gnome open-source project. Based on every imaginable XML-related standard (and
there are quite a few of them!), these function libraries give programmers a full range of XML capabili
ties. RexxXML brings most of these functions to Rexx programmers.
RexxXML has a wide range of features. Here are a few of the things you can do with it:
□Hsdsdkddstnlarsdctnds
□Dscdodncacrddascrstnl
□Dscdodncacrddasdcrcttnl
□oSLTasrscdsstnl
□Sctdocancctecctsn
□CsoodntccctsnsaotctaHTTP cneaFTP sdrndrs
□Cnccnldcldatncdreccd
292
XML and Other Interfaces
Here are the RexxXML functions along with brief descriptions (see the RexxXML documentation for
more detailed code-oriented descriptions):
□ xmlError nteturns error message text since the most recent caoo
293
Chapter 18
□ U)oenment SearSenrching — Thesnfunntiansapespetificallnconcednedhvithpngsindand cn-lyz-
ing document trees. They give scripts the ability tn inspect documents and better understand
their cnntents, withnst having tn lrngram these nleratinns exliicitic nr at iength in scrilts:
□ xmlevalexpression nEvaiutes XPath expressinn and returns resuit as a string
□XSLe PnoSassirg nThese functinns wnrk with and appiy XSLT styiesheets tn dncuments:
□ xmlparsexslt nParses and cnmpiies an XSLT styiesheet
□SSeaor Vrlidrtior nThese functinns autnmate the prncess nf schema vaiidatinn, that is, ensuring
that dncuments cnrrectiy match the specificatinns embndied in their reiated schemas:
□ xmlparseschema nParses a dncument schema
□HeeP rrd FeP nThese twn functinns retrieve data frnm URLs:
□ xmlPost nSends an HTTP post cnmmand tn a URL and returns resuit
□C-lrrgurga IrtanfrSa nThese twn functinns impiement the C-ianguage interface fnr the
RexxXML iibrary. The functinn iibrary can be used as a set nf caiiabie rnutines frnm C prngrams,
as weii as frnm with Rexx scripts:
□ rexxxmlinit nRegisters the XML iibrary and initiaiizes
294
XML and Other Interfaces
RexxXML is tested with the Regina Rexx interpreter. It can be used with other Rexx implementations
that can register and load external functions but is not formally tested with them.
RexxXML downloads as a single compressed file containing either binaries or source. Downloads
include a complete guidebook in Adobe *.pdf format. Entitled RexxXML Usage and Reference, it is writ
ten by the author of the product, Patrick T. J. McPhee. The guide offers an excellent introduction to XML
and related subjects like XPath, XSLT, and schemas. It also contains the complete function reference
manual and a quick reference guide.
The first step in installing RexxXML is to download and install its prerequisites, the libxml and
libxslt products. These free products are distributed under the MIT License and are downloadable at
http://xmlsoft.org. They are available for almost any operating system as either binaries or source.
The decompressed files include installation instructions that follow typical procedures for Windows,
Linux, or Unix. Documentation for the products is at http://xmlsoft.org/docs.html . That Web site
also offers good tutorials and introductory information on XML programming, the XML standards,
dialects, related standards, and the like.
Download and install RexxXML. Download sites include www.interlog.com/~ptjm and www.
interlog.com/~ptjm/software.html. As with libxml and libxslt, if any Web addresses have
changed, merely enter the product name into a popular search engine such as Google or Yahoo! to locate
other download sites. RexxXML installs in the same manner we’ve seen in previous chapters covering
interfaces such as Rexx/SQL, Rexx/Tk, Rexx/DW, and Rexx/gd. The RexxXML Windows Dynamic Link
Library, or DLL, is named rexxxml.dll; the Linux or Unix shared library has the same root name. As
always, ensure that the proper environmental variable points to the library directory so that the inter
preter can find and load the external functions.
Common operations
We’ve mentioned the wide variety of operations scripts can perform using the RexxXML functions. In
this section, we’ll review short code snippets that show how to perform several of the most common
operations. The specific operations we’ll explore include:
295
Chapter 18
First, let’s look at how to load the RexxXML external function library for use. A script must register and
load the RexxXML library prior to using any of its functions. This is accomplished in a manner similar to
registering and loading the Rexx/Tk, Rexx/DW, or Rexx/gd libraries:
All RexxXML functions are now available to the script. Invoke the xmldropfuncs function to deregister
the library and free resources when all XML processing is complete.
Many scripts load, process or transform, and save an XML document. Figure 18-1 diagrams how this
interaction typically occurs.
Processing Documents
xmlFreeDoc
An optional argument specifies whether the Document Type Definiton, or DTD, should be referenced and
the XML file validated. Here are a couple examples:
Applying xmlparsexml to a document that is not well formed returns 0. The same return code indicates
an attempt to validate an invalid document.
After processing or transforming the file, the script can write it to disk:
Always free resources after finishing with the data by calling the xmlfreedoc function:
296
XML and Other Interfaces
Now that we’ve seen how to load, validate, and save XML documents, let’s see how to create them. This
is just a matter of string processing—splicing and pasting data together with the proper descriptive
tags. Assuming that the data is in the chunks array, here’s how to build a simple, prototypical XML doc
ument. Each data item is written with the proper beginning and ending descriptive tags:
data = ‘<data>’
do j = 1 to number_of_chunks
data = data || ‘<chunk>’ || chunk.j || ‘</chunk>’
end
data = data || ‘</data>’ /* add the end tag */
Of course, building XML files can be much more complicated than our simple example. RexxXML pro
vides the full set of required functions.
To add data to an existing document, a script creates the right type of node and inserts it into the
proper location within the document tree. xmladdelement is the function to use. Other useful
functions for adding information to documents include xmladdattribute, xmladdtext, xmladdpi,
xmladdcomment, xmladdnode, and xmlcopynode. Figure 18-2 summarizes how scripts can add or
remove data from documents through appropriate RexxXML functions.
Updating Documents
Figure 18-2
Processing an XML document means understanding and parsing its tree structure. This allows scripts to
search, analyze, and transform XML documents. One approach to processing a document is to expand
its document tree into a Rexx array or stemcontaining all the relevant data. The xmlexpandnode func
tion accomplishes this. After issuing this function, scripts can traverse the data in the tree and analyze
the document. You can access attribute values by using a tail for the stem that names the attribute.
297
Chapter 18
XPath is another option for searching document trees and analyzing data. Figure 18-3 below diagrams
how programs use XPath to process documents.
Figure 18-3
The xmlfindnode function returns a node setrepresenting a document subtree, from which individual
items can be extracted by xmlnodesetitem. xmlnodesetcount tells how many items are in a node set.
Look at this example:
This code starts with the document tree or nodeset off the root element. It converts each eligible node in
the document into an array and calls the routine named process to work with these nodes.
Validating documents against schemas(or data definitions) is another important operation. Schemas are
read from files, Rexx variables, or the XSD environment. The four main functions applied to them are
xmlparseschema, xmlvalidatedoc, xmlfreeschema, and xmldumpschema.
The xmlError function returns the accumulated error and warning messages since the last time it was
called. Use it as a generic function to return further information when an error occurs.
298
XML and Other Interfaces
Applying XSLT stylesheets is as easy as document validation, in that it only involves four functions:
xmlparsexslt, xmlfreestylesheet, xmlapplystylesheet, and xmloutputmethod. XSLT data can
be read from a file, read from a Rexx expression, or taken from the environment. Figure 18-4 diagrams
how scripts can apply stylesheets to documents.
Here is an example that shows how to apply stylesheets to documents. It applies the stylesheet named in
the first parameter of the xmlapplystylesheet function to the document named in the second. That
function returns the result tree:
The final two statements in this example free the stylesheet and the document after processing is com
plete. In working with XML it’s a good idea to always free resources after the script has completed pro
cessing the items.
A sample script
RexxXML ships with a couple dozen sample programs. All come complete with appropriate *.xml,
*.xsd, *.xsl, and *.html input files. The bundled manual RexxXML Usage and Reference discusses sev
eral of the examples in detail.
Let’s take a look at an sample script. It appears courtesy of the author of RexxXML, Patrick T. J. McPhee.
This sample script is called iscurrent.rex. It reads an HTML Web page. It scans the Web page and
extracts a data element from items in a table in that Web page. The data item it extracts is the most
299
Chapter 18
current version for the RexxXML software package. The script compares that version to the one the
script itself is using. If the Web version is newer, the script informs the user that a newer version of
RexxXML than the one he or she is using is available.
This script demonstrates how to access an HTML Web page, how to scan it, and how to extract informa
tion from it.
if rcc then do
say rxfuncerrmsg()
exit 1
end
call xmlloadfuncs
software.html = ‘http://www.interlog.com/~ptjm/software.html’
sw = xmlParseHTML(software.html)
if sw = 0 then do
say xmlError()
exit 1
end
/* software.html has a single table. Each row of the table has the
package name in the first column, and the version number in the second.
The first occurrance of the package is the most current one. */
300
XML and Other Interfaces
if rcc then do
say rxfuncerrmsg()
exit 1
end
call xmlloadfuncs
This initialization code would typically appear at the start of any RexxXML script. It is very similar in
design to the code in previous chapters that loads and registers other external function libraries, for
example, those for Rexx/SQL, Rexx/Tk or Rexx/DW. Note that the rxfuncerrmsg function is specific
to Regina Rexx. It returns the most recently occurring error message. If you’re not using Regina, leave
this statement out your code. We recommend replacing it with your own error message concerning the
problem.
Following initialization, this line specifies the URL (or Web page address) of the HTML Web page to
analyze:
software.html = ‘http://www.interlog.com/~ptjm/software.html’
This next line retrieves the version of RexxXML the script is running under:
The version this statement retrieves will later be compared to that on the Web page. If they differ, the
script knows that a newer version of the RexxXML package is available and reports that finding in its
concluding statements.
This code parses the HTML Web page into a variable as a document tree. A return code of 0 means that
parsing failed, and results in a call to xmlError for more information:
sw = xmlParseHTML(software.html)
if sw = 0 then do
say xmlError()
exit 1
end
Now that the code has retrieved the Web page HTML and parsed it into a document tree, it must inspect
a table within the HTML that lists software packages and their versions. The header of the table
describes its contents:
<thead>
<tr>
<td width=”20%”>Package</td>
<td width=”10%”>Version</td>
<td width=”20%”>Date</td>
<td width=”50%”>Notes</td>
</tr>
</thead>
301
Chapter 18
The next program statement retrieves the row from the table that refers to the most recent version of the
RexxXML package. The program knows that the first occurrence of the package in the table in the most
recent one. It uses the following statement, with its XPath expression, to retrieve the appropriate infor
mation from the table:
Now, this statement extracts the Version data element from the row just retrieved. Together with the
previous statement, this operation requires coding that is a bit detailed. For right now, just concentrate
on what the statements do. They retrieve the appropriate data element, the RexxXML version, from the
HTML table:
The final lines of the program compare this version to that under which the script runs. It displays a
message as to whether they differ:
This sample script illustrates how easy it is to process and analyze a Web page or document with the
RexxXML functions. The package offers much more capability than can be shown here. It saves a lot of
string processing work that scripts would otherwise have to perform themselves in order to process
XML. Readers are urged to download the RexxXML product, review its examples, and read the
documentation.
You can locate most of these tools simply by entering their names as search keywords in any search
engine, such as Google. Code repositories such as SourceForge and GitHub host many of these tools. Or go
to www.RexxInfo.org for a single point of access to most of them.
302
XML and Other I nterfaces
Summary
This chapter explores the free RexxXML package for processing XML files. RexxXML provides almost
any function needed to work with XML, HTML, XPath, XSLT, and related data description languages.
Combined with the string manipulation strengths of Rexx scripts, RexxXML makes XML processing
quick and straightforward.
This chapter defined what terms like XML, XSLT, XPath, and HTML mean. We investigated the kinds of
XML processing that the RexxXML package makes possible. We briefly discussed the functions in
RexxXML, including those for document tree parsing, document tree searching, XSLT processing, and
schema validation. And we discussed specific XML operations, such as how to load and process XML
documents, how to process documents, how to validate documents, and how to process them against
XSLT stylesheets. We reviewed one of the sample scripts that ships with the RexxXML package. The
sample script demonstrates how to access a Web site, download an HTML page for processing, and how
to scan that Web page and extract a relevant data element. In all, we saw that RexxXML is a comprehen
sive package that makes working with XML and its related technologies much simpler.
There are dozens of other add-in interfaces and tools for Rexx developers. In Chapters 15 through 18, we
discussed a few of the most widely used packages and demonstrated how to use them. While we could
only skim the surface of these products in the limited space available, the material did provide an intro
duction sufficient to get you started with the tools. Appendix H lists several dozen more free and open
source Rexx interfaces and tools along with brief functional descriptions. Most can be found for free
download on the Internet merely by entering their names into any popular search engine. New Rexx
function libraries and utilities are continually being produced.
303
Part II
Evolution and
Implementations
Overview
You have reached the point in this book and in your understanding of Rexx that you know the
core language. Now it is time to explore more deeply the many platforms, problems, and
situations to which Rexx applies. Let’s expand our knowledge into advanced Rexx.
This chapter outlines the history and evolution of Rexx. Discussing the evolution of the language
shows how it has migrated across platforms and addressed new developer needs over time. This
is useful in analyzing where Rexx fits into your own organization and how you can assess its
utility as a universal scripting tool.
This chapter analyzes the roles Rexx fulfills, as a scripting language, macro language, shell
extension, application programming interface, object-oriented scripting tool, mainframe command
language, and vehicle for programming handheld devices. It discusses where different Rexx
implementations fit into this picture. It describes the “personalities” of the various Rexx
intepreters and when and where you might want to use each.
It also introduces a methodology for comparing Rexx interpreters to one another. The
methodology can be used, too, for comparing Rexx against other scripting and programming
language alternatives. Different projects and different organizations have different needs. No one
interpreter is best for every situation. This chapter helps you compare and contrast interpreters
and discusses some of the roles of the various Rexx products.
This chapter also introduces the remainder of the book. The chapters that follow, Chapters 20
through 30, discuss specific Rexx interpreters. Each describes the personality of an interpreter,
where it runs, how to download and install it, and its features that extend beyond the Rexx
standards.
The upcoming chapters offer sample scripts that demonstrate many of the interpreter extensions
and how they can be leveraged on specific platforms. The goal is to provide you with interpreter-
and platform-specific information.
Chapter 19
Rexx interpreters run the gamut of platforms and applications. Some emphasize portability, while
others emphasize platform-specific extensions and leverage platform-unique features. Some target cell
phones, while others target mainframes. Some are object-oriented, while one offers an alternative to
Java programming (it runs on the Java Virtual Machine and generates Java bytecode).
The upcoming chapters go beyond the fundamentals of classic Rexx covered to this point and expand
into the extended features of Rexx as it runs on different platforms. But first, we take a step back. We
need to understand how Rexx evolved, why different interpreters are available, and some of the
differences among them.
Let’s start by discussing how Rexx was invented and how it has evolved. This helps us understand why
there are many different Rexx interpreters today, and how they came to be. Understanding the larger
picture is useful in assessing the various uses of Rexx and the differences among Rexx interpreters.
Rexx was invented in 1979 by Michael Cowlishaw at IBM’s UK laboratories. It evolved into its present
form in the early 1980s. Rexx stands forREstructured eXtended eXecutor. Rexx is sometimes written in all
uppercase as REXX.
Rexx was designed to fulfill the promise of scripting languages — as general-purpose, high-productivity,
easy-to-use vehicles for the quick solution of programming problems. Rexx was designed to be easy to
use, yet powerful and flexible. These two design goals — ease of use and power — normally conflict.
The specific goal in Rexx was to bring them together. This contrasts with the goals of many other
scripting languages, which include:
□ Aedresgiagacpecificbiomiem ceace
□ Fulfilling tho'gersonalgoals os tastfs oftzlac^nr inventors
□ moovpatlbyiity with eoriion iansgosges
□ Opvlmlzlfl mscolfes leslulces (CPU cycles ll memlly)
□ Eo Oase nt mteopreter writing
TOo goat wltO Roxx was to develop a nonoroi-ourooso language that would bo used by tOo widest
olsslbte crlss-sectlln lf oelote ond tlutd fet toelr needs. Presclentty ontlclootlnl olt toe Internet
would drlro cooooratlro softwaro doroloomont moro tOan a docado lator, lnrontor CowllsOaw offorod
froo uso of Roxx wltOln IBM’s notwork and sollcltod suggostlons and rocommondatlons. Ho got lt ln
abundanco, froquontly answorlng Oundrods of omalls wltO ldoas orory day. TOls foodback was crltlcal
ln sOaolng tOo languago as usor-frlondly yot ooworful. TOo rosult ls tOat Roxx Oas lts own unlquo
oorsonallty among scrlotlng languagos.
308
Evolution and Implementations
Any programming language ultimately faces the popularity test. As Rexx spread throughout IBM,
IBM’s customers became aware of the language and demanded it. IBM complied by shipping Rexx as
the scripting language for its VM mainframes. Soon IBM bundled Rexx with all versions of its main
frame operating systems, including those in the OS and VSE families.
In the 1990s, IBM developed a strategy for common software across all their computers. They called it
Systems Application Architecture, or SAA. IBM selected Rexx as its official command procedure language across
all its operating systems. The result was that IBM has bundled Rexx with all its operating systems ever
since, including their mainframes (OS, VM, and VSE), midrange servers (IBM i, previously known as
i5/OS and OS/400) and Power Systems, and personal computers (PC-DOS and its follow-on OS/2, which
eventually evolved into eComStation and then ArcaOS).
Others soon picked up IBM’s enthusiasm for the language. Microsoft offered Rexx as the
Windows scripting language in the Windows 2000/XT Resource Kits. (The company later dropped
Rexx in order to proprietarize Windows scripting. It did this by promoting its own tools such as
VBScript and later PowerShell). Several other systems, including the Amiga and its descendants,
bundled Rexx as their default OS scripting language.
By the 1990s, Rexx had become quite popular. But it was still widely considered an IBM product — even
if this view was not entirely accurate. Two events transformed Rexx from an IBM language into a
universal scripting language:
The resula as ahia aotiy free Rexx runs on varauilly every known pliaform, from cell phones int
aibleas, ao lipaops int teskaops, ao matringe servers of ill kants, up ao ahe lirgesa mianfrimes. There
ire very few pliaforms on whach Rexx toes noa run. There ire ia leisa tozen free Rexx anaerpreaers int
Rexx as uset worltwate.
Fagure 19-1 pacaorailly taspliys ahe cross-pliaform versiaalaay of ahe free Rexx anaerpreaers.
Rexx runs on ill aotiy’s mijor pliaforms: Wantows, Lanux, Unax, BSD, micOS, cell phones, int
mianfrimes.
Bua jusa is sagnaficina as ahia aa ilso runs on miny obscure int lesser-known sysaems. (Thas even
anclutes obsoleae pliaforms, whach as hinty af you hippen ao saall suppora one.) There ire more of
ahese ahin we cin lasa here, bua ahey anclute DOS fimaly sysaems, OpenVMS, CISC, eComSaiaaon,
ArciOS, OS/2, Mic OS X, clissac Mic OS, Amagi OS4, AROS, ieROS, MorphOS, AOS, Aahos/Syllible,
Q(X, BeOS, Pilm OS, SkyOS, Symbain OS/EPOC32, Pockea PC, Wantows CE, Wantows hinthelts,
int more.
For i linguige ao ichaeve ahas success, aas language definition musa coilesce ia ahe ragha aame. If i
linguige as locket anao tefinaaaon aoo eirly, aa miy noa evolve sufficaenaly int cin ossafy anao i saunaet
form. On ahe oaher hint, af i linguige fials ao icquare formil tefinaaaon an aame, aa cin frigmena anao i
aower of bibble. Or, when i formil tefinaaaon toes come ilong, ahere miy be so miny ancompiaable
versaons an use ahia ahe saintirt toes noa mein inyahang. (The BASIC linguige as in eximple. There
ire so miny nonsaintirt versaons of BASIC ahia ahe officail linguige tefinaaaon meins lattle.)
309
Chapter 19
Rexx was lucky. Its inventor wrote a book to provide an informal language definition early on. The Rexx
Language by Michael Cowlishaw (Prentice-Hall, 1985) crystallized the language and provided de fact
direction for various implementations. With minor revisions, it was republished as a second edition in
1990. This edition is commonly referred to as TRL-2 or TRL2.
American National Standards Institute promulgated their ANSI Rexx standard in 1996. The standard
follows TRL-2 very closely and ties up a few minor loose ends. The ANSI-1996 standard finally gave
Rexx the imprimatur and prestige of an international standards body. It is formally known as X3.274-
1996 and was developed by ANSI technical committee X3J18. Chapter 13 enumerates the exact
differences between the TRL-1, TRL-2 and ANSI-1996 standards.
Rexx is both well documented and highly standardized. This makes possible portable programming across
the extremely wide variety of platforms on which Rexx runs. Chapter 13 made some recommendations
about how to maximize the portability of Rexx scripts. These fundamentals also make skills
transferable. If you can program Rexx on one platform, you can program it on any platform.
With the rise of object-oriented programming in the 1990s, two free object-oriented Rexx interpreters
came out. IBM developed Object REXX and KilowattSoftware created roo! Both are super sets of
classic Rexx; they run standard Rexx scripts without alteration. IBM’s Object REXX became an open
source product in early 2005. It is now called Open Object Rexx - or ooRexx -- and is managed as an
open source project by the non-profit Rexx Language Association.
310
Evolution and Implementations
As Java became a popular language for developing Web-based applications, Michael Cowlishaw also
developed NetRexx, a Rexx-like language that runs in the Java environment on the Java Virtual
Machine (JVM). NetRexx scripts synergistically coexist with Java code. NetRexx scripts use Java classes
and NetRexx can be used to create classes usable by Java programs. NetRexx can be used to write
applications, servlets, and Java Beans. NetRexx is also a free and open source product.
Scripting languages are high-level vehicles that glue together other software components for
quick program development. At their best, they result in higher programmer productivity; faster, easier
debugging; and, more readable, maintainable code. Figure 19-2 enumerates some of their benefits:
Benefits: Drawbacks:
Figure 19-2
The downsides to interpreted languages are that they are less machine-efficient. They might not be the
best choice for an application demanding the absolute highest performance. An example might be an
online transaction processing (OLTP) system. In this case, the extra effort coding and debugging with a
compiler might well be worth the higher machine efficiency you gain.
Another example might be coding a low-level device driver, or a frequently used internal part of an
operating system. These applications might require the lower-level detailed control a language like C or
C++ or Java affords. Scripting languages aren’t generally used for programming that requires low-level
machine access.
311
Chapter 19
□ Thh rieh of thh inthrnht rhqeirh. nhw orogramming tooler Scrioting ohrfhctly fite thh whb
orogramming oara.igm, for both ehrvhr- an. clihnt- ei.h .hvhloomhntr
□ Thh rieh of frhh an. oohn eoerch eoftwarh (FOSS) hae ohrmanhntly changh. thh orogramming
landscape. Meet popular scripting languages - like Rexx - are FOSS.
An. eo ecrioting langeaghe arh eeh. for almoet any kin. of aoolication to.ay:
Scripting Uses
* Web browser programming
* Web server-side applications
* Glue languages
* GUI programming
* Command language
* Job control
* Systems administration
* Task automation
* Text processing
* Macro programming
* Embedded programming
Figure 19-3
312
Evolution and Implementations
One fundamental division is between those languages that are general-purpose versus those that are
more narrowly focused on a specific design problem. Look at figure 19-4:
Figure 19-4
You can see that some of the special-purpose languages support web development. Others are for text
processing or embedded programming. Specially designed scripting languages can be ideal for such
tasks.
You might disagree with some of the classifications in the charts we present in this section. That's
because it's not always clear how to classify languages. And languages change and evolve over time.
PHP is a great example. It was originally designed exclusively for server-side web programming. It
ran on servers to dynamically generate web pages. But it expanded its purview over time, and
today can be used for client-side development as well. It can even be used for applications other than
web programming (though one rarely encounters it in this role).
PHP highlights another another useful distinction. Some scripting languages were designed specifically
for web programming, while others address other kinds of applications. Some cover both bases, while
others specialize.
This chart shows some of the more common scripting languages for web development. We've split
them into server- versus client- side tools. You can see that Rexx fits with those general-purpose FOSS
languages used for server-side development, whereas the client-side tools are specialized tools:
313
Chapter 19
Figure 19-5
Another way to categorize scripting languages is to divide them into open source and proprietary products.
Rexx sits squarely on the side of FOSS, though there are a few commercial versions around:
Figure 19-6
Another way to judge scripting languages is by their relative ease of use. "Ease of use" really encompasses a set
of related values. Is the language easy to learn, easy to code, and easy to maintain? Does it put the burden of
programming on the computer or the programmer?
Figure 19-7
314
Evolution and I mplementations
Languages that one might consider easy include Rexx, ooRexx, NetRexx, Python, PHP, JavaScript, GML, and
forms of Visual Basic. More difficult languages include several whose challenging syntax can be difficult to
grapple with. The Unix/Linux shell languages, Perl, and AWK, fit into this category. They're quite powerful, but
their idiosyncratic syntax means they aren’t easy to use or maintain.
A final distinction is between languages that are cross-platform, versus those that are identified with a particular
operating system. Many cross-platform examples spring to mind here, including Rexx, ooRexx, NetRexx, Python,
Perl, Ruby, PHP, JavaScript, and others. Most are open source.
Of course there are languages that are cross-platform, but in practice are rarely seen outside their native
environment. An example would be PowerShell, which runs on several platforms, but is still nearly always
encountered (and considered by most to be), a Windows tool.
Another example is the Linux/Unix/BSD shell languages. Most run on several platforms but are rarely seen
outside their native environment. Thus there’s a distinction between those languages that can be used across
platforms, as opposed to those you truly see employed in this manner.
Figure 19-8
Interpreter Options
As you know, the examples in this book were tested using the Regina Rexx interpreter under Windows and
Linux. We recommended that you start with Regina because it is an excellent open source Rexx that will run on
any platform. Regina meets all Rexx standards and is the most popular free Rexx interpreter. It enjoys the
largest user community and more interfaces are tested with Regina than any other Rexx interpreter.
The other Rexx interpreters offer benefits, too. It is time to explore them. There are about a dozen free Rexx
interpreters. The following table lists the platforms, costs and licensing terms for all these products. Some are
free regardless of the use to which you put them, while others are free for personal and nonprofit use but
require a license fee for commercial use. Some are open source, while others are free but not open source. Be
sure to read the license for any Rexx interpreter you download and agree to its terms before using the product.
A convenient list of free software licenses is maintained by the Open Source Initiative or OSI at
www.opensource.org/licenses .
315
Chapter 19
ooRexx Windows, Linux, Unix, BSD, Free, open source, GPLv2, CPL 1.0
(aka Open Object Rexx) MacOS
BRexx Linux, Unix, BSD, macOS, DOS, Free, open source, GPLv2
Android, Windows
Which Rexx?
Given that there are so many free Rexx interpreters, an important question is which one should you choose? The
answer varies by project and by organization because different projects and organizations each have their own
criteria. The weighting or relative importance of those criteria vary.
For example, one organization might rank Windows GUI programming as their primary concern. Regina or Open
Object Rexx (ooRexx) might be their choice. Another organization might cite object-oriented programming as their
goal. ooRexx and NetRexx are their prime candidates.
316
Evolution and Implementations
No one interpreter is best for all situations — different projects have different criteria and will result in different
“best” decisions. To determine which Rexx interpreter(s) are right for you, follow these steps:
Many organizations use thisweighted-ranking approach to evaluating software. List all selection
criteria in a spreadsheet and assign each criterion a rank (such as High, Medium, or Low, or a
number between 1 and 10). Give each interpreter a rank for how well it fulfills each criterion.
Multiply all criteria ranks by the weightings to derive an overall number for each product. The
interpreter with the highest number best fits your requirements.
The table below shows how the weighted ranking approach works. Criteria for the project or
organization are listed on the left-hand side of the chart, along with the priority for each. These will
vary by project or company, as will their relative importance. Of course, your ranking chart will
probably list more criteria than shown here; this is just an example of how to create the chart.
Criteria (Rank) Regina BRexx ooRexx Rexx/imc NetRexx ... others ...
TOTAL SCORES:
The free Rexx interpreters are listed across the top. Each column reflects the score for the interpreter versus
the criteria. Multiply the score for each box times the rank or weight and add all rows to score a cumulative
weighted ranking for each interpreter. This cumulative ranking is written for each interpreter in the last row
of the chart.
Weighted-criteria comparison is useful for other kinds of evaluations in computer projects. For example, use it
to determine which scripting language is most appropriate for a project or an organization. Rank Rexx versus
other general-purpose scripting languages such as Python, Perl, VBScript, the shell languages, Ruby and others.
Just as in the comparison of different Rexx interpreters, different criteria and weightings tend to promote
different tools as most the suitable for different projects.
317
Chapter 19
The goal here is to offer brief high-level profiles of each product in separate paragraphs. Chapters 20 through 30
offer much more detail on the specific interpreters, their platforms, applications, and language extensions.
We emphasize that the material in this chapter is merely a starting point for your own investigations. Different
projects and organizations have different needs. There is no one answer for everyone. Only you know what your
needs are, so only you can select the right tool for the purpose you have in mind. With this background, let’s briefly
profile the different Rexx interpreters.
Regina
Regina is the most popular free Rexx, and it runs on nearly all platforms. Its large user community guarantees
good support and means that more interfaces and tools are tested with Regina than any other Rexx. Regina is an
open source product distributed under the GNU Library General Public License.
Regina includes a very wide variety of extensions: it interfaces to just about every tool, from GUIs to databases to
XML or whatever you need. The external function libraries and interfaces it works with include advanced C-like I/O,
SQL database I/O, Tk GUI, DW GUI, gd graphics, XML, JSON, ISAM I/O, and many others.
Regina also offers platform-specific features. So if you work on popular systems like Windows or Linux it brings
many OS-specific features to the table. It can even run as part of a shell (the Z shell).
Regina is one of the few free Rexx interpreters that fully implement the SAA application programming interface, or
API. This provides a well-documented interface between Rexx and other languages. This allows Regina to be
employed as a set of callable routines. It gives Regina a documented, standardized interface to Rexx extensions
whether they ship with Regina or other Rexx interpreters.
Regina’s documentation is very detailed and contains perceptive insights on Rexx interpreters and the finer points of
the language. It explains Rexx standards and compatibility issues, how to use the SAA API, error conditions, I/O, the
stack and language extensions. Chapter 20 discusses Regina in detail.
BRexx
BRexx is one of the fastest Rexx interpreters and it features a tiny disk footprint. BRexx runs under Linux,
Unix, BSD, macOS, mainframes, Android, Windows, and several other systems.
BRexx is especially well suited to systems with limited resources, such as cell phones, handheld devices, and
smaller or older systems. It runs natively under Android, tiny Linuxes, DOS, Windows handheld versions
like Windows CE, and similar small-footprint platforms. Its facility with DOS is not to be ignored - DOS is
still popular for programming embedded systems and specialized devices, especially where a real-time
single-tasking OS is required.
On Android, BRexx uses a universal Android scripting interface called Scripting Layer for Android (or SL4A).
318
Evolution and Implementations
SL4A allows developers to automate Android tasks in scripting languages instead of using Java.
The advantage, of course, is that Rexx has simpler syntax than Java. Also, it’s much faster and
easier to develop with an interpreted scripting language than a compiled language like Java.
Chapter 25 explains how to use BRexx on Android and provides sample programs.
For all platforms, BRexx comes bundled with a number of external function libraries that go beyond
the Rexx standards. Among its facilities for various platforms come built-in and external functions
for math, PC file I/O, ASCII-EBCDIC conversion, C language style input/output, console I/O, array
I/O, Windows CE, DOS interfaces, and ANSI-terminal screen I/O. BRexx also includes functions for
VM/CMS-like buffer management, interfaces for databases like MySQL and SQLite, date/time
conversions, and CGI scripting.
Like Regina and Rexx/imc, BRexx features a long, proven support history. Chapter 22 describes
Brexx and presents sample BRexx scripts. Chapter 25 specifically covers its use for Android
programming.
BRexx/370
This is the BRexx code, ported and adapted to mainframe operating systems. One GitHub-based project targets
CMS (VM/370) while the other targets MVS (OS/VS 3.8). Both run on the Hercules emulator, an open source tool
that allows you to run mainframe operating systems on personal computers and other platforms. (Learn more
about Hercules at http://www.hercules-390.eu/.)
The MVS version of BRexx/370 includes support for VSAM/KSDS, formatted screen, TCP support, NJE38, etc.
The CMS version is integrated into CMS and runs memory-resident. Both products give you the ability to
isssue operating system commands and support the features you'd expect from Rexx running in the
mainframe environments. Appendix Z tells where to download these products, how mainframe emulation
works on your PC, and where Rexx fits in.
Rexx/imc
Rexx/imc runs on BSD, Unix, and Linux operating systems. It includes extra functions for C-like
I/O, higher mathematics, Unix environmental info, and other C-like and Unix-friendly extensions.
Rexx/imc is a partial implementation of the SAA API. It has a proven track-record of support
spanning decades. It seems to be most popular in the BSD community. Chapter 21 describes
Rexx/imc and offers a few sample scripts that illustrate its extended capabilities.
319
Chapter 19
You can download and install Rexxoid from the Google Play Store. At its GitHub home, at
https://github.com/Jaxo/yaxx, you’llfind a half dozen sample programs as well as product screen shots.
When installing and configuring Rexxoid, it may be helpful to connect the phone to a laptop or desktop
computer via USB cable. This also facilitates debugging.
Rexxoid has a fundamentally different design than BRexx for Android. While BRexx uses the SL4A scripting
interface, Rexxoid is primarily intended to issue shell commands. From this perspective, it’s a little more
narrowly focused product. We describe Rexxoid and discuss sample programs in chapter 25.
cREXX
cREXX might best be characterized as an experimental interpreter project. To paraphrase from its GitHub
home page, the project’s goals are to:
□ Devolep a modem immlemenOation oR e Kent intrepreten and compllro from the unounp up
□ See tl Rexx ctn be tmtroved nwtee retttntnl twe essence ol twe etnlntle
Intttteey, cREXX rnns on twe z/VM mttnlrtme tettlorm. Twe lote tsto teso mtke tt tvttetbee on Wtndons,
Ltnnx, mtcOS, tnd z/OS mttnlrtmes.
Gtven cREXX’s somenwtt extertmentte nttnre, tnd tn tnttcttttton twtt tt ntee evoeve tnd cwtnle too rtttdey
lor t trtnted resonrce etke twts book to keet nt nttw, ne non’t cover twts troject tn dettte.
Interested retders tre enconrtled to vtstt twe troject tt tts GttHnb wome ttle ol
https://github.com/adesutherland/CREXX.
Reginald
Reltnted nts ortltnteey btsed on Reltnt Rexx. Reltnted tddstooes tnd lnncttons twtt ttteor Rexx
stectficteey lor Wtndons trolrtmmers.
For extmtee, tt tncendes t Wtndons tnstteeer, t GUI scrttt etnncwer, tnd t Rexx-tntre text edttor. It comes
nttw t GUI dtteol tens Intelrtted Deveeotment Envtronment (IDE), tnd weets deveeoters to crette
trolrtms lettnrtnl Wtndons GUI tnterltces. Its mtny otwer tnterltces tncende twose lor MIDI fiees,
comtntertzed steecw syntwests, Wtndons fiee tnd dtrectory I/O, tnd TCP/IP sockets. Its lnnctton etbrtrtes
snttort trtnscendentte mttw, relnetr extresstons, steecw lenerttton, tnd vtrtons nttetttes.
For deveeoters ntswtnl to crette Rexx scrtttstwtt moed setmeessey tnto Wtndons, Reltnted fits twe btee. It
offers t lree “toner tooe” lor Wtndons deveeoters tnd snttetes teetwe lnncttons Wtndons deveeoters need.
It’s teso ntceey docnmented.
Unlortnntteey, tt twe ttme ol nrtttnl, snttort seems to wtve ettsed lor Reltnted. Reltnted tstwns "lrozen tn
ttme" tnd lteetnl bewtnd Wtndons deveeotments. Cwttter 23 descrtbes Reltnted tn dettte tnd tresents t len
stmtee scrttts.
320
Evolution and Implementations
r4
r4 is a product of Kilowatt Software, which also offers the upwardly compatible, fully object-oriented
roo! Both are customized for Windows, with easy installation and developer tools specifically
designed for that operating system. The support tools and utilities work with both r4 and roo!
r4 and roo! offer GUI tools and accessories that are simpler to use than many Windows GUIs. Some
of the tools include a UI forms tool, color textfile viewer and editor, over 135 windows command
tools, visual accessories, XML to HTML auto-converter, GUI widgets, and so on.
The products each include some 50 sample scripts and introductory tutorials that bring to life the
spirit of Rexx as an easy-to-learn language. This documentation helps beginners learn the languages
quickly and experienced developers to become immediately productive.
Unfortunately, at the time of writing, Kilowatt Software no longer appears to be supporting r4 and
roo!. Like Reginald, they are falling behind Windows' evolution. We retained the material on r4 and
roo! in this edition because we do still field occasional inquiries about them. Chapter 26 describes
both products, along with programming examples.
These products can run “classic” procedural Rexx scripts without alteration. In addition they support
all the features of object-oriented programming through their additions to standard Rexx. Both
support inheritance and derivation, encapsulation, polymorphism, and abstraction. They come with
complete class libraries.
There is one major difference between the two ooRexx products and roo! — they are completely
different implementations of object orientation in Rexx. Their class libraries, new instructions and
functions, and even their object-oriented operators differ. Those interested in object-oriented Rexx
programming should look closely at the differing approaches and compare them against their
requirements.
We might add that there is no ANSI standard for object-oriented Rexx. A standard would give object-
oriented Rexx greater importance as a universal language, encourage wider use, and might increase its
availability on other platforms.
Object-oriented Rexx is popular both for classic Rexx programmers who wish to transfer their skills
to object-oriented programming, and for those who know OOP but seek an easier-to-use language
than many of the alternatives. Like classic Rexx, object-oriented Rexx combines its ease-of-use with
power. But in this case, the power is based on extensive class libraries.
321
Chapter 19
The Rexx Language Association renamed the product Open Object Rexx, usually referred to as ooRexx. Open
Object Rexx is upwardly compatible with classic Rexx and extends the procedural language into the object-
oriented world.
ooRexx differs from roo! in that both are object-oriented supersets of classic procedural Rexx that take very
different approaches to implementing object-oriented programming.
It’s important to note that ooRexx supports all advanced OOP features. That includes multiple inheritance and
operator overloading, for example.
ooRexx runs on Windows, Linux, Unix, BSD, macOS, and other platforms. It is definitely the most widely used
object-oriented Rexx interpreter. It also runs on Android through the "ooRexx for Android" project.
Chapters 27 and 28 describe ooRexx in detail, and walk you through a number of example programs. If you’re
not familiar with object-oriented programming, this is the perfect way to learn. Use your knowledge of Rexx
and expand it at the pace that suits you into the object-oriented world.
Appendix I lists the ooRexx classes. Viewing that list will give you an idea of the extensive power ooRexx offers.
As a port, it features all the classes, methods, etc, available in ooRexx. The only difference is that it specifically
targets Android, instead of the other platforms ooRexx supports (Windows, Linux, BSD, Unix, macOS, etc.).
At the time of writing, scripts can only be run on a rooted device and require the Android Software
Development Kit (SDK). This may change in the future.
roo!
Kilowatt Software offers their object-oriented extension of classic Rexx called roo! It is a companion product to
their classic Rexx interpreter r4.
roo! adds all object-oriented features to classic Rexx, including a full class library and support for all object
programming principles. Upwardly compatible with r4, it uses all the same tools. Good tutorials bridge the gap
between classic Rexx and object-oriented programming.
Unfortunately, like r4, roo! appears to be out of support from Kilowatt Software. It is thus falling behind Windows'
evolution. Since some readers still ask about it, chapter 26 describes roo! in detail.
322
Evolution and Implementations
NetRexx
NetRexx is an free and open source “Rexx-like” interpreter that runs wherever Java runs -- on a Java
Virtual Machine, or JVM. In fact, NetRexx was the first language (after Java itself) to run on the JVM.
NetRexx offers an alternative way to script Java applications with a language that is closely
modeled on Rexx. It uses the Java class library. You can code NetRexx scripts that call Java
programs, or vice versa, or you can use NetRexx as a complete replacement or alternative to Java
programming. You can even code Java Beans in NetRexx and use it for server-side programming as
well. NetRexx is an easier-to-learn and use scripting language than Java that jettisons Java’s C-
language heritage for the clean simplicity of Rexx.
NetRexx is not a superset of classic Rexx. It is best described as a Rexx-like language for the Java
environment. Since NetRexx is not upwardly compatible with classic Rexx, there is a free standard Rexx
to NetRexx converter program available called Rexx2Nrx. Find it at www.RexxInfo.org or enter
keyword Rexx2Nrx in any Internet search engine.
Michael Cowlishaw - the inventor of classic Rexx - also developed NetRexx. Today, the product is free
and open source, and is developed and maintained by the Rexx Language Association. The RexxLA
offers a free license to download and use NetRexx for all Java environments. Chapter 30 describes
NetRexx in detail.
Mainframe Rexx
Rexx was originally developed in the early 1980s to run under the VM family of operating systems.
Later in the decade IBM released Rexx for OS- and VSE-family mainframe systems as well.
Rexx has now been the dominant mainframe scripting and command language for decades. It
integrates with nearly all mainframe tools and facilities and is the primary mainframe macro
language.
Some of the mainframe systems with which it interfaces include: TSO, CMS, ISPF, Xedit, editor
macros, MVS Batch, SYSREXX, RACF, CCA, SMF, SDSF, NetView, CICS, DB2, CONSOLE, GCS,
LINK*, ATTACH*, SYSCALL, IPCS, CPICOMM, CPIRR, LU62, APPCMVS, OPENVM, Zowe, and
all address spaces or environments.
That’s quite a list. You can see why Rexx is irreplaceable as a mainframe administrative or systems
management tool. Of course, it's quite popular for user applications, too.
Mainframe Rexx is so widely used that IBM long ago introduced a Rexx compiler. So developers
can ultimately compile their programs for greater runtime efficiency once they have thoroughly
debugged them.
Mainframe Rexx is not free or open source but comes bundled with the operating system. Chapter
29 discusses mainframe Rexx. It lists the differences between it, the ANSI-1996 standard, and the
free Rexx implementations. This helps those who are transferring either their skills or their code
from mainframes to other platforms. It may also prove useful to those who know systems like
Windows, Unix, or Linux and have occasion to work with mainframes.
323
Chapter 19
You might wonder: aren’t mainframes fading away? Do people still use them? Are mainframe skills still
a viable career path?
IBM doesn't release sales numbers. The result is that you'll find no lack of passionate arguments for
either side of this question on the internet. Our own sense of it is that we're in a steady state.
According to statistics from several sources, at the time of writing, somewhere over 70% of Fortune 500
companies have mainframes. They are vital platforms in many industries, including banking and
finance, retail, insurance, healthcare, government, and transportation. Mainframes process over 90% of
the world's credit card transactions, for example.
Surveys shows that large companies that have mainframes nearly always keep them.
Yet the once common scenario of new companies growing into mainframes is probably a story of the
past, now that there are alternatives like cloud services, parallel Linux servers, and other options.
Some argue that Rexx will fade as a mainframe language as the “baby boomer” generation that grew up with
it retires. That doesn’t seem to be the case. Younger programmers in countries that support outsourcing work,
such as India, are keeping Rexx alive and healthy. Those who think that the use of mainframe Rexx is shrinking
may be mistaking shifts in staff locations for a decline in usage.
Chapter 29 covers mainframe Rexx. It discusses the unique characteristics of mainframe Rexx and how it
differs from many of the free Rexx implementations. As well as mainframe professionals, this discussion may
be of use to those who transfer either their skills or their code between mainframes and other platforms. It may
also prove useful to those who know systems like Windows, Unix, or Linux and have occasion to work with
mainframes.
□ AppacCixoQo--oRaxxo<-->oCliitoiquivalaccai
□ AppacCixoRo--o-aicirafaoRaxxovi thaoAiti-1996ottacCarC
□ AppacCixoto--oJnboictarviaHoQoaitinci
□ AppacCixoZo--oiRaxx/370oinro-aicirafaoifolatinc
324
Evolution and Implementations
ARexx
ARexx is a Rexx interpreter originally written by William S. Hawes and bundled with the Amiga
computer line in the 1980s. While the Amiga computer is today little known, it was considered a very
innovative machine for its time and had quite a mindshare impact. Amiga’s success was based on its
offering the first popular multi-tasking operating system for a personal computer. The OS coupled
these advanced features with the easy programming provided by Rexx as its primary scripting
language.
While the Amiga was popular and highly respected in its day, the company behind it, Commodore
International, ultimately fell into decline and faded away in the 1990s.
Yet the great popularity of the Amiga ensured its survival. Today there are several successor operating
systems that keep the Amiga spirit alive and run ARexx:
□ .c'ROr AorRPROS)
rll taese are leve srsteos taat are used acd suhh(rted. Taer ruc (c a vareetr (y hlaty(ros eccludeca
vare(us o(dels (y roeaa, P(WerPC, rhhle oac’s, acd roeaasce. h(oe (y tae ucderlreca hr(cess(rs ecclude
Ir-32, Ictel, rRM, o68k, P(WerPC, acd rRM. Y(u cac aet a secse (y tae levelecess (y taes c(ooucetr br
veseteca (ce (y ets c(de reh(set(rees at roecet at https://AmiNet.net.
ArcaOS Rexx
rarleer We oecte(ced taat IBM bucdled Rexx Weta sh/2 yr(o ets eccehte(c. Taes aaeced tae lacauaae
oacr ceW adaerects acd resulted ec tae devel(hoect (y a larae vareetr (y Rexx hackaaes acd
ecteryaces.
Ulteoatelr, (y c(urse, sh/2 l(st taeyao(us fsh Warst (y tae 1990s t( Wecd(Ws. But Waele et Was sWeht
yr(o tae leoeleaat, sh/2 c(ctecued (cas a veable hlaty(ro.
rvectuallr IBM tracsyerred sh/2 devel(hoect acd suhh(rt t( a c(ohacr called eC(ohtate(c, (r eCh.
Later taat c(ohacr tracsyerred sh/2 t( ets currect devel(her, rrca N(ae. T(dar tae (herateca srsteo es
called rrcash, acd et c(ctecues t( bucdle acd ruc Rexx, as ded all ets hredecess(rs.
325
Chapter 19
Shell language scriptsrun in the same process as the command interpreter. The advantages to this are:
□ Envnroement variebles set by the script remanr in etfeft after ties tenmination.
□ The cuntent wonking el t rectory csei bs cl tncseeoernennen tly by ths script.
□ The nuteric resurn code of she lcriss il eveilesle in she resurn code veriesle idensiiied st
the special shell vesieble (in some operating systems named $? ).
One selkekink tf Rekine Rexx eneslel it tt sen in the lete sstlell el the Z-lhell ( zsh)r Thil tielnl the
ebove edventegesr This is esefel fos scsipts thet need to eltes the eses’s envisonment, fos exemple, fos
scsipts thet pesfosm psodect setep, instelletion, os cestomizetionr If yoe need to sen e scsipt nithin the shell
itself, Regine cen feliill yoes seqeisementsr
Rexx As an API
Sevesel Rexx intespsetess ese implemented es e libsesy seiteble fos linking into othes epplicetionsr This
ellons psogsems nsitten in othes lengeeges to employ Rexx es e set of celleble soetines thet psovide the
fenctionelity inhesent in Rexxr
Rexx intespsetess ese e consistent intesfece stendesd, celled the SAA APIos sometimes theRexx APIr These
inclede Regine, Open Object Rexx, Regineld, end nith pestiel complience, Rexx/imcr The Regine Rexx
docementetion meneel hes e complete section thet expleins hon this nosksr
326
Evolution and Implementations
The documentation is written from the standpoint of C/C++ programmers. The IBM Rexx SAA
manual may also be of interest to those using the Rexx API. See Appendix A for details on how to
obtain these sources of API information.
In most cases, you can install more than one Rexx interpreter on the same machine without experiencing any
conflicts. Just follow the normal installation procedures, then use the products in the usual manner. The
author routinely installs both Regina and ooRexx on the same computer without any issues.
There are a few aspects of which to be aware. First, you’ll need to ensure that each interpreter’s executable
has a unique name. This is important because a few of the products use the same name for their interpreter.
For example, versions of Regina, Rexx/imc, and ooRexx all call their executable rexx. If you install these
three interpreters into a shared directory this creates an obvious conflict.
Secondly, when you install more than one interpreter on a single computer, you’ll want to ensure you are
running the interpreter you think you are in any particular situation. It is possible, for example, to implicitly
run a script, and have that script executed by a different interpreter than the one you thought ran it. For
example, if you install several Rexx intepreters on a Windows machine, you’ll want to know which interpreter
runs a particular script when you double-click on it.
The solution to the first problem is simply to ensure that each Rexx executable has a unique name. Installing
the interpreters under different directories resolves this issue because the fully qualified path name is the
filename for the executable. This approach works fine if you want to place each interpreter in its own unique
product directory.
If you want to place multiple Rexx interpreters in the same directory, you’ll have to rename any executables
whose names conflict. An example where this might be an issue is if you use Linux operating system
and want to install all language interpreters into the same shared executables directory. The solution is simply
to ensure that each Rexx interpreter in that directory has a unique name for its executable. For example,
versions of Regina and Rexx/imc both call their executable rexx. You might delete Regina’s rexx binary and
use its regina binary instead. Or rename Rexx/imc’s binary to rexximc.
327
Chapter 19
Once you have installed multiple Rexx interpreters, any standard Rexx script will run under any of the
interpreters. Still, you’ll probably want to know (or ensure) which interpreter executes when you run a script. One
solution is to explicitly invoke scripts. Name the interpreter to run on the command line with the name of the Rexx
script to execute as its input parameter. For example, this command runs a script under the r4 interpreter on a
Windows machine. r4’s executable is named r4:
r4 rexxcps.rexx
This command runs the same script on the same computer under BRexx. This BRexx executable is named rexx32:
rexx32 rexxcps.rexx
If necessary, use the full path name to uniquely identify the executable to run. This technique is useful if the
unqualified or “short” filenames of the interpreters are the same but their fully qualified path names differ. Here is an
example that specifies the full path name of a Rexx interpreter on a Unix machine. The name of the executable is
rexx, a potentially duplicate short filename, but the fully qualified pathname ensures that the interpreter you want
runs:
usr/local/bin/regina/rexx rexxcps.rexx
If you don’t fully qualify the executable name, the operating system’s search path may dictate which binary runs.
Take this command:
rexx rexxcps.rexx
Assuming that there are multiple rexx executables on the machine, the operating system has to choose
which to run. Most operating systems have an environmental variable that tells which directories to search
for the binary and the order in which those directories are searched. This environmental variable is named
PATH (or path) under Windows, Linux, Unix, BSD, and other operating systems. In terms of the example,
the first reference in the search path that resolves to a valid executable named rexx is the one the operating
system runs.
Under the Linux, Unix, and BSD operating systems, you may direct which interpreter runs a script by coding
some information on thefirst line of the script. Thefirst line of the script begins with the characters#! and
specifies the fully qualified name of the Rexx executable. For example, to run the Rexx/imc interpreter the first
line in each script will typically be its fully qualified executable’s name:
#!/usr/bin/rexx
The fully qualified path name and the name of the Rexx executable vary by the interpreter. If two
interpreters use the same fully qualified name for their executable, change the name of one of them to
distinguish it. This unique reference then becomes the first line in scripts that use that interpreter.
Under operating systems in the Windows family, the file association determines which Rexx interpreter
executes a script when you click on that script. File associations are established for the interpreters
automatically when they are installed. Here are the defaultfile associations for some Rexx interpreters you
might install under Windows:
328
Evolution and Im piementations
BRexx .r
NetRexx .nrx
You can set the file associations manually (or alter them) through the Windows' file Explorer. Go to the top
menu bar, select Tools | Folder Options... | File Types, and you can view and optionally change file
associations. Or access this tool by searching for the keywords "associating files" in the Windows Help
system. Managing the file associations allows you to avoid any conflicts in assigned file extensions among
the Rexx interpreters.
The chance of a conflict when installing two or more Rexx interpreters on the same computer is
low. If you do have a conflict, verify that each Rexx interpreter has a unique name; try explicitly
running the interpreters using their full pathnames; verify that PATH (or path) variables are
correct; verify that the libraries the interpreters require are in the correct directories; and, verify
the Rexx script extensions.
Java Integration
You can commingle Rexx with Java in several ways. We've already mentioned NetRexx, the Rexx-like
language that runs on the Java Virtual Machine. Use it to code applications, beans, and servlets.
In addition, the free and open source tools BSF4ooRexx andBSF4Rexx bring the full capabilities of Java to
ooRexx and classic Rexx, respectively.BSFstands forBean Scripting Framework. These tools enable you to
exploit all Java classes, objects, and functionality from within ooRexx and classic Rexx. Chapter 24
provides examples of using BSF4ooRexx for programming single board computers. Appendix T covers Java
integration and the Bean Scripting Framework in detail.
Summary
In this chapter, we took a step back and described the invention and evolution of Rexx. The goal was to get a
bit of perspective on the language's platforms, roles, and applications.
This chapter summarized the various Rexx interpreters that are available. It profiled their strengths, the
platforms on which they run, and their major characteristics. We also presented a methodology for deciding
which interpreter is most suitable for a particular project or application. Different projects and organizations
have different needs, and the methodology helps structure decision-making when selecting a product. The
methodology can be used either to compare Rexx to other scripting languages or to select a Rexx interpreter
from among the several that are available.
329
Chapter 19
This chapter also discussed the roles that Rexx scripts fulfill within organizations. These include using Rexx as
a macro language, calling Rexx functions from other languages through the Rexx API, running Rexx in the
shell, scripting cell phones and handheld devices, and Rexx in its role as the mainframe command language.
Several object-oriented Rexx interpreters are available. In an extended and altered version called NetRexx, the
language also participates in the Java environment, providing both client- and server- side scripting that fits
synergistically with Java.
Finally, this chapter addressed how to resolve any conflicts that might occur when installing more than one
Rexx interpreter on the same machine. Conflicts rarely occur, but when they do, they can easily be resolved.
With this chapter laying the groundwork, let’s discuss the organization of the remainder of the book.
Chapters 20 through 30 describe different Rexx interpreters. Each chapter focuses on a different interpreter
and describes its advantages, the platforms it supports, and its unique “personality.” The chapters describe
the features each interpreter offers beyond the Rexx standards and illustrate many of these extensions
through sample scripts. The goal of these chapters is to take you beyond classic Rexx and into the realm of
advanced scripting. You’ll improve your scripting and also become privy to the advanced and interpreter-
and platform-specific aspects of the language.
Several of the upcoming chapters cover specific kinds of programming. For example, three chapters address
object-oriented Rexx scripting. They include an object-oriented tutorial that leverages your knowledge of
classic Rexx to take you into the world of object-oriented programming. Two other chapters cover single
board computers and Android cell phone programming. Yet another chapter focuses on NetRexx, a Rexx-
like language that complements Java. NetRexx fits into the Java environment and runs under the Java Virtual
Machine. And one chapter summarizes Rexx as the predominant mainframe command language. Several
appendices also specifically discuss aspects mainframe programming in detail.
Let’s go forward and learn about the various Rexx interpreters and how they apply to a wide range of
platforms and programming problems.
330
""
Regina
Overview
Regina is the most widely used free Rexx interpreter. Its use is truly worldwide: it was originally
developed by Anders Christensen of Norway in the 1990s and is today enhanced, maintained, and
ported by Mark Hessling of Australia. Regina’s popularity is well deserved. It meets all Rexx
standards and goes well beyond them in the features and functions it offers. It runs on nearly any
platform. Some of its many advantages were presented in the first chapter, when we described why
it is an excellent choice of Rexx interpreter.
This chapter explores those aspects of Regina that go beyond the ANSI standards to give you a feel
for the extra features of this product. Since Regina meets all Rexx standards, of course, everything
from the tutorials of the earlier chapters of this book apply to your use of Regina. In fact, all the
sample scripts to this point were tested with Regina in its role as a standards-compliant Rexx inter
preter. The goal here is a bit different. This chapter specifically explores the Regina features that go
above and beyond the Rexx standards.
First, we’ll cover the advantages of the Regina interpreter. Then we’ll discuss when and why it
may be appropriate to employ the features of Rexx interpreters that extend beyond the
standards, and what the downsides of this decision are. Different projects have different goals,
and you want to be appraised as to when using interpreter features beyond those of the Rexx
standards is advisable.
This chapter describes Regina’s extended features. These include its many operands for the
options instruction, additional functions (for bit manipulation, string manipulation,
environmental information, and input/output), facilities for loading external function libraries, the
stack and its uses, and the SAA API. Let’s dive in.
Chapter 20
Advantages
In deciding whether to use any particular Rexx interpreter, you’ll want to know what the benefits to the
product are. Do its strengths match your project’s needs? This section attempts to answer that question
for the Regina Rexx interpreter. This chapter lists and discusses many of the product’s stengths. The
chapters that follow this one present the same information for other Rexx interpreters including
Rexx/imc, Reginald, BRexx, r4, and roo!, Open Object Rexx, ooRexx for Android, mainframe Rexx, and
NetRexx. A separate chapter covers each interpreter, and each chapter begins with a list of the product’s
special strengths, similar to the one given here. Use these lists as your “starting point” in deciding which
Rexx interpreter is right for any particular situation or project.
With this said, let’s discuss the strengths of Regina. Figure 20-1 pictorially summarizes many of Regina’s
advantages as a Rexx interpreter.
Runs Everywhere
Figure 20-1
332
Regina
□ Regina boasts tools and interfaces — As the most popular free Rexx, most Rexx tools are written
for and tested with Regina. Open-source interfaces that work with Regina include those for
relational databases, the Tk and DW GUI toolkits, the gd library for graphics, the THE editor,
ISAM access method, Curses for standard screen I/O, CURL for URL support, and many others.
□ Regina offers compatibility with other Rexxes — Regina includes features and functions that go
beyond the standards to provide compatibility with other Rexx implementations. We call this
Regina’s supercompatibility. Take issuing operating system commands, for example. Regina
supports ANSI-standard redirected I/O via the address instruction. It also allows the
address instruction to use the stack, as in mainframe and other Rexxes. Where Rexx
implementations differ, Regina supports all the approaches and lets the developer choose
which to use.
□ Regina includes functions from other Rexxes — As part of its supercompatibility, Regina
includes implementation-specific functions matching those supplied by VM mainframe Rexx,
the SAA standard, Amiga ARexx, and OS/2 Rexx.
□ Regina offers detailed documentation — Regina’s documentation is probably the most
comprehensive and detailed of any free Rexx interpreter. Its documentation goes far beyond
the required and includes detailed discussions of conditions, I/O, extensions, and the stack. It
includes a large section on the Rexx SAA API, which explains to developers how they can call
Regina as a set of external routines.
□ Regina is open source. Regina is open source and is distributed under the GNU Library General
Public License. Not all free Rexx interpreters are also open source.
□ Regina supports the REXX API — Regina is implemented as a library based on the standard Rexx
application programming interface, or API. It interfaces to programs written in other programming
languages based on this clean, well-documented interface. With this interface, for example, you
could code C or C++ programs that employ Regina as a set of Rexx functions.
□ Regina is international — Regina supports several spoken languages, including English,
German, Spanish, Portuguese, Turkish and Norwegian.
□ Regina uses DLLs and shared libraries. Regina includes the Generic Call Interface, or GCI, which
allows scripts to call functions in Windows DLLs or Linux/Unix/BSD/macOS shared libraries
as though they were Regina built-in functions. This gives Regina scripts full access to external
code of all kinds for Windows and Linux- and Unix- derived systems (even though that code
was not specifically written to be invoked from Rexx scripts).
□ Regina is thread-safe — This allows it be used by applications like the Apache Web server
which use threads for superior performance.
□ Regina supports “superstacks” — Regina’s stack services extend way beyond the required or
expected. As described in Chapter 11, Regina’s external queues can be used for communications
between different processes on the same computer, different processes on different computers,
or even between different processes across the Internet.
Regina’s Extras
Chapter 1 discussed where to download Regina from and how to install the product. The chapters fol
lowing that one presented a progressive tutorial on how to script with standard Rexx. All this material
applies to Regina, and all the sample programs up to this point were tested using Regina. Now it’s time
to explore the extended features of Regina. This figure summarizes them:
333
Chapter 20
Figure 20-2
The extended Regina features include interpreter options, additional functions, and features for compati
bility with other Rexx interpreters and platforms. These additions beyond the Rexx standards offer great
power and are a strong advantage to Regina. But always carefully consider whether you wish to code
beyond the Rexx standards. Coding within the standards ensures the highest degree of code portability.
It ensures that others who know Rexx will be able to work with your code. Coding outside the standard
subjects your scripts to a greater risk of desupport. Standard Rexx will enjoy use and support forever,
whereas the extensions of any specific interpreter will always be subject to greater risk. We generally rec
ommend always coding with the Rexx standards. Including this extended material in this and subse
quent chapters on Rexx interpreter extensions is not meant to promote the use of these extensions. It is,
rather, intended to provide introduction and reference to the extensions for those who may need them
and understand both their benefits and their drawbacks.
There are very good reasons for Rexx interpreter extensions. They offer platform-specific features and
power that can be very useful for scripts that will run in specific environments. They also support cross
platform portability in ways not possible otherwise. As one example, porting mainframe scripts to other
platforms may be easier when the interpreter on the target platform supports mainframe Rexx exten
sions. As another example, programmers familiar with the I/O model of other programming languages
may prefer the C-like I/O offered in many Rexx extensions to the standard Rexx I/O model.
The bottom line is this: use nonstandard Rexx extensions if you need them for some reason, and you
have considered any potential downsides and have judged them acceptable. With this said, let’s enumer
ate and discuss Regina’s extended features.
Interpreter options
Recall that the options instruction allows you to issue commands to the Rexx interpreter. These options
dynamically alter the interpreter’s behavior. What options are available depend strictly on the Rexx inter
preter. Regina’s options instruction includes about two dozen parameters that scripts set to alter or direct
the interpreter’s behavior. These key ones dictate compatibility with various platforms and standards:
334
Regina
Option Result
CMS Engbies the set of extensuons expected un the VM/CMS BgunfrgBe envuronBent
UNIX Engbies Unux coBBgnd unterfgce fnnctuons. Thus uncregses perforBgnce over
nsunt the coBBgnd unterfgce to ussne sheii coBBgnds
VMS A set of unterfgce fnnctuons to the VMS opergtunt systeB. Thus Bgkes ut possubie
to scrupt the sgBe kunds of gctuons un Retung Rexx on VMS gs one wrutes un
VMS’s DCL igntngte
BUFFERS Mgkes gii VM-bgsed bnffer fnnctuons gvguigbie to Bgnupnigte the stgck
AREXX_BIFS Mgkes gii ARexx bnuit-un fnnctuons gvguigbie
REGINA Lgntngte ievei 5.00 pins Retung extensuons gre gvguigbie
ANSI ConforB to igntngte ievei 5.00
SAA ConforB to SAA stgndgrds
TRL2 ConforB to TRL-2 stgndgrds
TRL1 ConforB to TRL-1 stgndgrds
Regina includes other options beyond those listed. We do not mention them here because most direct the
interpreter in very specific ways or towards implementing very specific features. Often these control the
internal behaviors of the interpreter. The options listed in the table manipulate the more specific options
as groups of behaviors in order to gain compatibility with specific environments or standards. If you
need to investigate the low-level options, the Regina documentation spells them out in detail.
Functions
As an ANSI-1996 compliant Rexx, Regina has all standard Rexx instructions and functions. Appendices
B and C list these language elements in reference format with a coding level of detail. In addition,
Regina offers functions that go beyond the standards. Regina’s extra functions naturally group into sev
eral categories:
□ BiampnipuOntion
□ ntrimgmpnipuOntion
□ EnvironBental
□ Inpnt/ontpnt
□ SAAunterfgce to externgi fnnctuon iubrgrues
□ Stgck Bgnupnigtuon
These fnnctuons orutungted froB g nnBber of pigtforBs gnd other Rexx unterpreters. The foiiowunt tgbie
iusts Retung’s extended fnnctuons gnd shows froB whuch versuon of Rexx egch us deruved:
335
Chapter 20
Origin Functions
AREXX b2c, bitchg, bitclr, bitcomp, bitset, bitttst, c2b, close, compress, eof, exists,
export, freespace, getspace, hash, import, open, randu, readch, readln, seek,
show, storage, trim, upper, writech, writeln
SAA standard rxfuncadd, rxfuncdrop, rxfuncerrmsg, rxfuncquery
Regina cd, chdir, crypt, fork, getenv, getpid, gettid, popen, uname, unixerror, userid
VM (aka CMS) buftype, desbuf, dropbuf, find, index, justify, makebuf, sleep, state
OS/2 Rexx beep, directory, rxqueue
Appendix D alphabetically lists all Regina-specific functions with detail suitable for reference and cod
ing. It includes coding examples of the use of each function.
In the next several sections, you’ll learn about each group of functions. The goal here is simply to famil
iarize you with the names of the functions and what they do. For a complete alphabetical list of func
tions with full coding details, see Appendix D.
Bit manipulation
Bit manipulation refers to the ability to inspect and alter individual bits. Usually, the bits are manipu
lated in groups of 8 (one character or byte) or 4 (one hexadecimal character). Chapter 6 on strings briefly
considered why bit string manipulation can be useful, while Chapter 7 offered a sample script that per
formed key folding based on bit strings.
Standard Rexx offers a set of bit manipulation functions that include bitand, bitor, and bitxor. These
are backed up by bit conversion functions like b2x and x2b. Regina extends this power by adding another
group of bit functions, all derived from Amiga Rexx, a Rexx interpreter that was the bundled standard with
the Amiga personal computers. These functions are very useful when intense bit-twiddling is called for.
To access all of these functions, you must enable the options instruction string arexx_bifs, like this:
options arexx_bifs
336
Regina
String Manipulation
String manipulation is even more vital than bit manipulation, as amply pointed out in Chapter 6. Regina
offers a grab-bag of extra string functions. A few, like find, index, and justify, have mainframe ori
gins. These all have more standard equivalents, yet their inclusion is useful for mainframe compatibility
or for rehosting mainframe scripts.
Others of the string functions are just very useful and one wishes they were part of standard Rexx. hash
returns the hash attribute of a character string, while crypt returns the encrypted form of a string.
This table lists the extended Regina string manipulation functions. Where a function has a direct stan
dard Rexx equivalent, we have cited that equivalent. One should always code the standard Rexx func
tion if feasible:
337
Chapter 20
Environmental functions
The environmental functions offer a plethora of operating system services and also retrieve information
about the environment. Some of these functions are inspired by the PC platform, such as beep, cd or
chdir, import, export, rxqueue and storage. Others obviously come from Unix, such as fork,
getenv, getpid, gettid, popen, uname, unixerror, and userid. In all cases, the goal is the same: to
provide operating system services to Rexx scripts.
338
Regina
Input/output
Regina Rexx supports several I/O models. First, there is standard Rexx steam I/O, embodied in such
built-in functions as chars, charin, charout, lines, linein, and lineout. This model is simple, stan
dardized, compatible and portable.
Since some Rexx interpreters are actually implemented as C-language programs, they have added C-like
I/O. The “C-language I/O model” provides a little better control of files because they can be explicitly
opened, closed, positioned, and buffer-flushed. The modea file is opened in (read, write, or append) can
be specified. Direct access and explicit control of the read and write file positions is provided through
the seek function.
AREXX on the Amiga and the portable, free BRexx interpreter (see chapter 22) are known for supporting
the C-language I/O model. Regina does too. Regina offers it as a more powerful but less standard alter
native to regular Rexx I/O. Here are these additional built-in, C-like I/O functions:
Regina’s stream function supports all the ANSI-mandated information options, as discussed in Chapter
5 on input/output. The ANSI-1996 standard also permits implementation-specific commands to be exe
cuted through the stream function. In Regina these commands include the full range of options to con
trol file modes, positioning, flushing, opening, closing, status, and other operations.
In addition to standard stream I/O and C-like I/O, Regina can take advantage of several external pack
ages for sophisticated data storage needs. For example, the Rexx/SQL package supports connections to a
wide variety of databases including Oracle, DB2, SQL Server, MySQL, PostreSQL, and others. Chapter
15 illustrates Regina with the Rexx/SQL interface to relational databases. Another example is the open
source Rexx/ISAM package, which supports an indexed access method (ISAM).
339
Chapter 20
Let’s discuss how Rexx interpreters access these libraries of external functions. Regina makes a good
example of how to do this because it uses the Systems Application Architecture, or SAA, standards for
this purpose. Several other Rexx interpreters use the SAA standards as their interface mechanism, too.
Rexx external function libraries come in the form of shared libraries for Unix, Linux, and BSD operating
systems. For Windows systems they are Dynamic Link Libraries (DLLs).
Let’s discuss Unix-derived operating systems such as Linux, Unix, and BSD first. For Unix-derived oper
ating systems, external function libraries typically have names in one of these forms:
lib .a AIX
The underscores are replaced by the name of the library. For example, under Linux for a library called
funpack, it would be in the file libfunpack.so. Be sure to read the documentation for any product
you download, as library names can vary.
You configure your system for access to the external function libraries by setting an environmental vari
able. This specifies the directory that contains the function library to use. Here is the name of this envi
ronmental variable for several popular Unix-derived operating systems:
The “install notes” for Regina and the external library products typically list the names of this environ
mental variable for all Unix-derived operating systems, as needed. Be sure to consult the documentation
that downloads with the product for any changes or product-specific information.
340
Regina
The meaning of all this is, if you get a “not found” error of some sort when first setting up Rexx under
Unix, Linux, or BSD, or when first trying to access an external function library, make sure that the proper
environmental variable is set correctly.
On Windows operating systems, the DLLs are the equivalent of Linux/Unix/BSD shared libraries.
Windows DLLs reside in folders specified in the path environmental variable. The system shared DLL
folder is typically either c:\windows\system or c:\windows\system32, depending on the version of
Windows. The external function library DLL can alternatively reside in any other folder, as long as you
make the folder known to Windows through the path specification.
You install the Windows DLL or Linux/Unix/BSD library file and point to it with the proper environ
mental variable by interacting with the operating system. Then, from within Regina scripts, you use the
SAA-defined standard functions to give scripts access to these shared libraries or DLLs containing exter
nal functions. The built-in functions to use are:
rxfuncerrmsg Returns the error message that resulted from the last call to rxfuncadd
This function is a Regina-only extension.
Here’s an example that uses these functions to register external functions for use by a script. In this
example, the script sets up access to the external Rexx/SQL function library. The first line below uses the
rxfuncadd function to load (or “register”) the external function named sqlloadfuncs. The next state
ment then executes the sqlloadfuncs function, which loads the rest of the Rexx/SQL external function
library.
The name of the external library is encoded in the second parameter of rxfuncadd. In this case it is
rexxsql. rexxsql is the root of the filename of the file containing the external functions. For example,
under Windows, the full filename is rexxsql.dll. Under Linux it would be librexxsql.so.
sqlloadfuncs is the name by which this script will refer to that external library file.
The preceding code loads the Rexx/SQL external function library for use, but the same pattern of
instructions can be used to load a wide variety of other external libraries for use by scripts. For example,
Chapter 16 described how this code applied to Rexx/Tk, Rexx/DW, Rexx/gd, and other interfaces. Here
is how the sample Rexx/Tk script in Chapter 16 coded access to the external Rexx/Tk function library.
341
Chapter 20
Stylistically, the code varies a little from the preceding example, but the same pattern of two functions is
coded in order to access and load the external function library:
To summarize, we’ve discussed how to set up an external function library for use from within Regina
scripts. Then, we’ve seen the SAA functions you encode from within the Regina scripts to set up their
access to the external function library. Other SAA-conformant Rexx interpreters access external packages
in this same manner.
The stack
Regina supports all ANSI-1996 keywords on the address instruction for performing input/output to
and from commands via streams and arrays. These keywords include input, output, and error for
specifying redirection sources and targets, and stream and stem, to define whether the I/O goes to a
file or array.
Regina also permits the use of the stack for command I/O. In this it follows the lead of mainframe Rexx
and many other implementations.
The script in the section entitled “Using the Stack for Command I/O” in Chapter 11 showed how to send
lines to a command through the stack and retrieve lines from the stack using Regina. In addition, these
Regina built-in functions can be used to create or destroy new stack buffers:
To use these function, a script would normally begin by creating its own stack area through the makebuf
function. This function could be called repeatedly if there were a need for more than one stack buffer.
The script would invoke the dropbuf function to release any buffer after it is no longer needed. Or if
stack processing is complete, the desbuf function will destroy all extant stack buffers. The buftype
function is used mainly for debugging if problems occur. It displays the contents of all the stack buffers
extant at the time it is called.
As mentioned in Chapter 11, Regina permits an innovative use of the stack, through its “external queue
capability.” The stack can function as an interprocess communication facility between different processes
on the same machine, or between different processes on different machines. The facility even supports
communication between processes across the Internet. This goes well beyond the role of the stack as an
external communications data queue as supported by many other Rexx interpreters.
To use Regina’s extended external queue capabilities, install and start its stack service. Check out the
rxstack and rxqueue executables. Use the rxqueue extended function to manage and control the stack
from within Regina scripts.
342
Regina
Regina’s documentation provides a full explanation of how to do this. It tells programmers what they
need to know to use the Regina API. Of course, because it is written for programmers, the guide is tech
nical. It uses C-language syntax to explain the interface.
Sample Scripts
Subsequent chapters on other Rexx interpreters include sample scripts that demonstrate the specific
features of those interpreters and were run using them. We felt it was not necessary to include Regina-
specific scripts in this chapter for two reasons:
Subsebuent cnapters give many examples ef nei extended functiens can be used in Rexx scripts.
Appendix D prevides a cemplete ceding-level reference te all ef Regina’s extended functiens.
Summary
Tnis cnapter summarizes tne features ef Regina tnat ge beyend tne Rexx language standards. Regina
prevides functiens tnat duplicate mest ef tne extensiens feund in etner Rexx implementatiens. Specific
features ie described in tnis cnapter include Regina’s extra eperands fer tne options instructien, its
additienal functiens, nei ene accesses external functien packages using Regina, and tne SAAAPI and
its rele iitn tne Regina interpreter. Regina’s extra functiens are extensive, cevering bit manipulatien,
string manipulation, environmental infermatien and sentrei, and input/output. We aise described
Regina’s external bueue facility, er stack, and nei it can be used as a generalized cemmunicatiens veni-
cle betieen precesses regardless ef tneir lecatien.
Wnen censidering inetner te use tne extended features ef Regina (er tnese ef any etner Rexx inter
preter), ene must be fully cegnizant ef betn tne benefits and tne draibacks ef tnis decisien. Peier and
flexibility are tne advantages, but tne less ef standardizatien can be tne deinside. Eacn preject er erga-
nizatien nas its ein geals. Tnese must be measured against tne benefits and cests ef using tne extended
features.
343
Chapter 20
Regina is one of the world’s premier Rexx interpreters. It is the most popular open-source Rexx. It runs
on almost any platform. More developers use Regina than any other free Rexx interpreter, with the
result that more tools are tested with it and its support community is larger.
The next several chapters take a look at other Rexx intepreters. Those chapters spell out the strengths
and applications of the other interpreters. Some of the interpreters specialize in certain operating sys
tems or platforms, while others extend Rexx in entirely new directions. Examples of the latter include
roo! and Open Object Rexx, which support object-oriented scripting, and NetRexx, which brings Rexx
into the world of Java and the Java Virtual Machine. Stay tuned......
344
nt
_” 1
Rexx/imc
Overview
Rexx/imc is a standard Rexx for BSD, Unix, and Linux platforms. Written by Ian Collier of
England, some of the systems it is used on include all BSD distributions, all Linux distributions,
Oracle Solaris, AIX, HP/UX, Digital Unix, and IRIX.
Rexx/imc is at language level 4.00 and meets the TRL-2 standard. This chapter covers product
installation and the extended Rexx/imc features that go beyond the Rexx standards. These fea
tures give you power beyond the confines of standard Rexx but are, of course, less portable across
interpreters and platforms.
This chapter lists and discusses the strengths of Rexx/imc. Then it details how to install the prod
uct using popular package managers, and also from source code.
After this we describe the extended features of the product. These include functions for retrieving
environmental information, higher mathematics, SAA-based access to external function libraries,
and C-language-stylefile I/O. We illustrate the special features of Rexx/imc within the contexts of
two sample scripts. The first demonstrates some of the product’s functions for retrieving
environmental information, while the second shows how to use its C-like I/O functions. Let’s start
by reviewing Rexx/imc features.
Advantages
As we’ve mentioned, each chapter that covers a particular Rexx interpreter begins by listing and
discussing the unique aspects of that interpreter. Here are some key points about Rexx/imc:
□ eets standards— Rexx/imc is at language level 4.00 and meets the TRL-2 standard.
□U Uorxenrieneedastr—s ReRexmCimff offan u nbmber uf Unix-oHedtedfur-sresbeyond
the standard. These include C-like I/O functionsand Unix-specific environmental-
inemrnatimn eunCtimns.
Chapter 21
□ Additionaifudctioet ReRexmCimcindludesa set ofodozenmetmematicul functions and the
SAA- based functions to load and manage external functions.
□ Clear Uocwncndni— —Rexefimcinfludeedeas, epteplffo <UomemenSaSi on.Se cemws whh a nice
Rexx tutoriet cor begiooiog progremmeri. It ioCtufei e uiecut recereoCe iummery ei wett ei e
fomptete recereofe meoueti
□ Reliabtei welivrnvee w Tice Inteepretea hasOzun dsed for twoafeades and ie well pioven. It
has e toog track eecoed end is wett known end eespected io the Rexx community.
Installing Rexx/imc
Rexxeimc cen be ceeety downtoeded ceom seveeet sites. These inctude Pkgs.oeg et
https://pkgs.org/download/rexx-imc , the Rexx incoemetion website etRexxInfo.org, end
Ien Cottiee’s website https://www.cs.ox.ac.uk/people/ian.collier/Rexx/rexximc.html .
Since Web eddeesses sometimes chenge, just seeech foe the keyword Rexx/imc o e Rexx-imc on
Googte ic you need to.
Rexxeimc is disteibuted in seveeet foemets, inctuding RPM Peckege Menegee (.rpm) fites, end BSD
peckeges (.pkg).
If youe system hes e Peckege Menegee to instett such fites, meeety doubte-ctick on them to instett
Rexxeimc.
Aftee the instett, be suee to eeed eny instettetion notes, such es mey be conteined inREADMEoe
INSTALL fites. Foe exempte, you mey need to set youe PATH veeiebte to inctude the dieectoey of the
Rexxeimc executebte. And you mey etso need to set youe toed tibeeey envieonmentet veeiebte to give
Rexxeimc eccess to its sheeed tibeeey fite. The notes witt give you eny commends you need to
eccomptish this.
Fiest, doubte-ctick on the downtoedfite to decompeess it. If it ceeetes e .tarfite, doubte-ctick egein to
decompeess the .tar fite. You'tt know you'ee done decompeessing when you see e tong tist of
decompeessedfitesfly by.
Nevigete to the dieectoey into which you decompeessed the souece code, end took foe eny README oe
INSTALL fites. Reed those insteuctions, es they mey contein opeeeting system specific infoemetion
you'tt need foe e successfut instett.
Then, open e teeminet, switch to using the root usee id, end nevigete to the dieectoey into which you
decompeessed the souece code downtoed. Issue these commends es root:
346
Rexx/imc
./configure
make
make install
These commands configure and install the product. Since they compile source code, they require a C
compiler to run. Almost all Linux, Unix, and BSD machines will have a C compiler present. If your
system does not have one installed, download a free compiler from any of several sites including
www.gnu.org. For macOS, go to the Apple Store and download Xcode.
As in the package install, you should read any README files or install notes. They will likely tell you
to set two environmental variables. The PATH should be set to include the Rexx/imc executable. And the
library path variable should be set to include a pointer to Rexx/imc's shared library file.
This chart lists where Rexx/imc components typically reside. These vary by distribution, but most
often the directories are as follows:
Directory Contains
/usr/bin Location of Rexx/imc interpreter
/usr/lib Shared library location
/usr/man/man1 Location of the help files and documentation
/usr/share/doc Documentation, tools, services, libraries, and so on
/usr/share/doc/rexx-imc.__.__ Documentation for Rexx/imc including installation info
Based on this table, you would expect to ensure that your PATH variable contains/usr/bin, and
that your shareable load library environmental variable includes /usr/lib.
A good way to verify that the package installed correctly is to run the rexxcps benchmarking
program. Or create and run your own test script. Start your favorite editor and enter this
three-line test program:
# ! /usr/bin/rexx
/* a simple Rexx test program for Rexx/imc */
say 'hello'
347
Chapter 21
The first line should refer to the fully qualified directory name where the Rexx/imc executable is
installed. Save this code under the filename testme.rexx, then change the permission bits on that file
such that it is executable:
chmod +x testme.rexx
If you add the current directory to your PATH environmental variable, you can exclude the leading two
characters ./ and just run the script by entering its name:
testme.rexx
Featu res
Rexx/imc fully conforms to the language level 4.00 standard defined by TRL-2. Beyond this, it includes
additional functions, instructions, and features specifically oriented towards the Unix, Linux, and BSD
environments.
Rexx/imc extends standard Rexx by adding a number of extra built-in functions. Figure 21-1 pictorially
represents the categorization of these extra functions.
Let’s enumerate and discuss the extended functions with their coding formats so that you can see what
they offer. First, the Rexx/imc Unix-specific functions provide scripts information about their environ
ment. All provide features Unix- and Linux- based programmers expect:
348
Rexx/imc
Figure 21-1
The Rexx/imc mathematical functions support transcendental mathematics. These functions are very
similar to those you’ll find in other extended Rexx interpreters, for example, BRexx, and Reginald. Recall
that several add-on packages also include higher math functions. These include the Internet/REXX
HHNS Workbench, discussed in Chapter 17, and several of the packages listed in Appendix H. Here are
the Rexx/imc higher math functions:
acos(n) Arc-cosine
asin(n) Arc-sine
atan(n) Arc-tangent
cos(n) Cosine of nradians
exp(n) The exponential of n
ln(n) The natural log of n
sin(n) Sine
sqrt(n) Square root
tan(n) Tangent
topower(x,y) Raise xto power y
Rexx/imc supports the three IBM Systems Application Architecture (SAA) functions for loading the
using external functions. These are the same functions included in other Rexx interpreters, for example,
Regina:
349
Chapter 21
Code these functions in the same manner shown in the examples in Chapters 15 through 18 in order to
access external functions from Rexx/imc. The standard SAA calling interface makes a wide variety of
open-source interfaces available for use with Rexx/imc.
Rexx/imc offers a few additional miscellaneous functions. These support mainframe Rexx-style text jus
tification and conversions between binary and decimal:
In addition to its extra built-in functions, Rexx/imc enhances several instructions with additional conve
nience features:
□ sayn instruction writes a line without a carriage return (i.e., no linefeedor newline)
□ parse value instruction parses multiple strings (separated by commas) in one instruction
□ procedure hide instruction explicitly hides global variables from an internal routine
□ Arrays may be referenced by calculations. For example, you can code stem.(i+1), whereas in
standard REXX you must write j=i+1 followed by stem.j.
Rexx/imc accepts any nonzero number for true (not just 1). This means you can code C-style operator
less condition tests, where a function that returns any nonzero value evaluates to true:
if my_function() then
say ‘The function returned TRUE (any nonzero value)’
Recall that under standard Rexx, a condition must evaluate to either 1 (true) or 0 (false). In standard
Rexx, a nonzero value other than 1 is not acceptable and causes a runtime error.
Finally, Rexx/imc includes a stack that is used in traditional Rexx fashion. It can be used to communi
cate data between Rexx programs and between routines within a program. It supports all the stack
manipulation instructions (queue, push, pull, and parse pull), and the queued function to retrieve
the number of items on the stack.
350
Rexx/imc
Here are the additional Rexx/imc functions for C-language I/O. We have included their coding formats
so that you can see what parameters each function requires:
The C I/O model offers greater control of file input/output, at the cost of less standardized code.
Explicit control could be useful, for example, to close and reopen a file within a script, or when a script
opens so many files it needs to close some to reclaim memory. Of course, Rexx/imc supports standard
Rexx I/O as well.
Rexx/imc allows you to open a pipeto a shell command through the popen function and to close the
pipe by the pclose function. The pipe is an in-memory mechanism for communication between the
Rexx script and the shell command. Along with the system function this offers another mechanism for
communication with operating system commands.
351
Chapter 21
Other packages include Rexx/Tk, the cross-platform GUI toolkit, and RegUtil, an external function
library. More information on these interfaces and tools is available at the Rexx/imc download
webpages. Appendix H lists many additional free and open-source packages, tools, and interfaces.
#!/usr/bin/rexx
/*********************************************************************/
/* ENVIRONMENT */
/* */
/* This script uses some of Rexx/imc's unique environmental functions*/
/* It also shows results of PARSE SOURCE and PARSE VERSION. */
/*********************************************************************/
352
Rexx/imc
/* display PARSE SOURCE, VERSION, for Rexx/imc under Red Hat Linux */
The first block of code in the script retrieves and displays the current directory with the getcwd func
tion, changes the current directory to the root directory with the chdir function, then changes it back to
the original directory with chdir. The script’s output shows that the current directory was changed, and
then altered to its original setting, through these special functions:
The script uses the getenv function to retrieve and display the value of the username environmental
variable:
The script issues the Linux uname operating system command through the Rexx/imc system function,
and captures and displays its output. This shows how to capture command output into the script via the
system function. The special sayn instruction writes a string without a newline or linefeed character.
353
Chapter 21
This avoids an extra blank line in the program output, because the Linux uname command output con
tained a newline character:
Rexx/imc alternatively allows you to code the system command and capture its feedback in this manner:
The script retrieves and displays the user’s login id through the userid function:
Finally, the script displays the default command environment by the address function, and issues the
parse source and parse version instructions to display their results. The last three lines of the out
put disclose how Rexx/imc, and indeed, all standard Rexx interpreters, report on their environment and
the language version.
this is line 1
this is line 2
this is the last line
The program copies this file as is to the output and displays these lines on the screen while doing so:
The output shows both the read and write file pointer positions after each I/O operation. Pointer posi
tions include the line termination characters that occur at the end of lines. (Sometimes these are referred
to as the newline characteror as linefeedsor LF.) Remember that file positions always point to the next
character position to read or write. Here is the script:
#!/usr/bin/rexx
/*********************************************************************/
/* FCOPY */
354
Rexx/imc
/* */
/* Uses Rexx/imc I/O functions to copy a file. */
/* Reports file positions by the FTELL function. */
/*********************************************************************/
parse arg source target /* get filenames, retain lower-case */
exit 0
The script accepts two command-line input parameters. They specify the names of the input and output
files. These lines open the two files for use.
The second parameter on the open function is the modein which the file will be processed. The options are:
r Read.
w Write. If the file already exists, its contents are overwritten. If the file
does not exist, it is created.
a Append. If the file already exists, add new lines to the end of the file.
If the file does not exist, create it.
The third parameter on open is optional. If specified, it supplies the name by which the stream can be
referred to in subsequent file-oriented functions (such as linein, lineout, and close). This parameter
works whether quoted as in the example, or just supplied as an unquoted symbol. Either ‘in’ or in will
work. Consider this a file handle, or reference name, for the file.
355
Chapter 21
The do-while loop copies the lines from the input file to the outputfile. These lines in the loop show
the use of the ftell function to retrieve and print the current read and writefile pointers:
say 'Input file position after read #' j || ': ' ftell('in')
say 'Output file position after write #' j || ': ' ftell('out')
ftell is useful to programs that explicitly manipulate the file pointers. It gives them a way to verify the
current file positions. This could be useful, for example, for scripts that perform direct or random file I/O.
It’s also useful in scripts that read data from the samefile more than once or in programs that update
data within a file. The standard charout and lineout functions can also explicitly position a file
pointer. The stream function allows you to open a file in advanced modes for special processing.
The script checks the return codes from the close operations just as it did from the open functions.
This small step goes a great way to increasing program reliability. We have not included such error
checking in the examples of this book out of concern for program length and clarity. But in industrial
programming, the small effort required to check I/O return codes yields big dividends in program
reliability.
We recommend checking return codes from all input/output operations, whether using C-like I/O or
standard Rexx I/O functions.
Summary
This chapter summarizes some of the additional features offered by Rexx/imc beyond the TRL-2 stan
dard. Several of these features are especially useful to Unix and Linux programmers,fitting the tradi
tions and expectations of this programming community. Rexx/imc is a proven interpreter that runs on
any Unix, Linux, or BSD operating system.
The specific areas we discussed were the strengths of Rexx/imc and why you might use this interpreter.
Then, we described the typical installation process using common package managers, and also how to
build the product from source code.
We described Rexx/imc functions for retrieving environmental information, higher mathematics, SAA-
based access to external function libraries, and C-language style file I/O. Finally, we illustrated some of
the extended features of Rexx/imc in two sample scripts. The first demonstrated how to retrieve
environmental information, while the second showed how to perform C-like input/output.
356
Rexx/imc
357
99
____ I____ I
BRexx
Overview
BRexx is a free version of classic Rexx developed by Vasilis Vlachoudis, a nuclear scientist at
CERN laboratory in Switzerland. Written as an ANSI C-language program, BRexx is notable for
its high performance and small size. It also provides special built-in functions and external
function libraries that offer many extra features.
BRexx was originally developed for DOS decades ago. Evolving ever since, it has been used on
a very wide variety of operating systems including varieties of Linux, Unix, BSD, macOS,
Windows, and DOS. The BRexx/370 project offers an open source alternative to IBM’s Rexx for
mainframe operating systems. And, BRexx runs on Android as well, providing a good
example of how Rexx spans the gamut from cell phones to mainframes. (Chapter 25 provides
extensive coverage and programming examples of BRexx on Android.)
BRexx's outstanding feature is its tiny footprint. The entire product, including full documentation
and examples, totals only a few megabytes. That’s small enough to install almost anywhere.
And BRexx runs fast, too. In tests versus other Rexx interpreters, it's always one of the fastest.
This chapter describes the strengths of BRexx and how to download and install it. Then we’ll
discuss some of its special features that extend beyond the Rexx standards. These include extra
built-in functions, such as those for manipulating the stack, system information and control,
higher mathematics, C-language style input/output, and MySQL and SQLite database access.
BRexx shines in supporting several I/O paradigms, including standard Rexx I/O, C-like I/O, and
database I/O. We’ll discuss when each is best used and the BRexx functions that support each I/O
model.
We'll also discuss BRexx functions designed specifically for Windows CE. Windows CE is a
legacy system: Microsoft has announced that license sales will end in 2028. So you may choose
to skip this section unless you have a specific interest in Windows CE or Microsoft's related
"Windows embedded" operating systems for handhelds and cell phones.
We'll walk through several example BRexx programs that highlight its unique features. These
programs illustrate C-like I/O, SQLite database access, array I/O, and direct data access.
Chapter 22
We conclude with an example program that demonstrates BRexx's special DOS functions. While long
obsolete as a general purpose operating system, DOS lives on in embedded programming, device control
programming, and as a gaming platform for some 7,000 free DOS games.
Advantages
As in previous chapters that examine specific Rexx interpreters, we need to know what BRexx features
distinguish it from the alternatives. In this section, we briefly list and discuss some of the advantages to
the BRexx interpreter. These are some of the key strengths of the product:
□ Small footprinta — DibtrsSdted ac a gisgte momprsd aed files thn entire OisSdlloddroduct
requires only a few megabytes of dick spats.
□ Corratdvr atdrfeogearvr teeatedr avr fetmtiotv rn Striosc taa rra tommaadc or orogramc
toded ac if shey were fratsioac ac loag ac shoce rorsiaec rce csaadard I/O.
BRexx ic ecoetially crisable where mathiae recorrtec are limised. For examole, she arshor worked wish a
tharisy shastolletsed doaased oercoaal tomorserc, toafigrred shem, aad croolied shem so aeedy
individuaic. We needed So be able So write simple stripSc that would run on aimocS any PC, without having
she lrxrry of accrmiag shas she Pr had a workiag Iaseraes or LAN toaaetsioa, boosable USB oorsc, or aay
minimum amount of internal memory. We quitkly learned that BRexx ic oartitularly well cuited to cuth
cituationc. No worriec about available dick coate or memory tonctraintc. No tonfiguration ortomoatibility
iccuec. BRexx ic very fact co the ctriotc ran quitkly even on older mathinec.
BRexx fully meetc the TRL-2 ctandard and ic at language level 4.00. It alco goec well beyond the Rexx
ctandardc in offering many additional built-in funttionc, over a half-dozen external funttion
librariec, and other extended featurec dectribed in the following cettionc.
360
BRexx
BRexx packaging has varied over the years for different platforms. Sometimes there are platform-specific
downloads for Linux package installers, such as .rpm files for the RPM Package Manager, and .deb files
for Debian-based package managers. There have also been the equivalent setup.exe install files for 32-bit
Windows and Windows CE.
If any of these are available to you, just download the file, then double-click to run the package installer,
and you're done.
After downloading thefile, you double-click on it to extract all the files from the archive. (It's possible
that this only produces a .tar file, in which case, you need to double-click that .tar file to finally
extract all the files.)
Once you've got the binaries and other BRexx files in place, you may need to set two environmental
variables. As you would expect, one of them is your PATH variable. Make sure that the PATH on your
system includes the location of the BRexx executable.
For example, if you install the BRexx executable rexx into the directory /usr/local/bin, you would
want to see if that directory is in your PATH with this line command in Linux/Unix/BSD:
echo $PATH
export PATH=$PATH:/usr/local/bin
The other environmental variable you may need to set is RXLIB. This tells BRexx the location of its
library file (also called a shared library or load library). It may be easiest to locate the library file in the same
directory as the BRexx executable, rexx:
export RXLIB=/usr/local/bin
For Linux, Unix, and BSD systems, remember that you can code thefirst line of any script to
indicate the location of the BRexx executable. For example, if the BRexx rexx executable resides in
/usr/local/bin, thefirst line of your Rexx program would be:
#!/usr/local/bin/rexx
361
Chapter 22
By default, the file name extension for BRexx scripts is .r. So the name of the well-known Rexx
benchmarking script would be: rexxcps.r
Double-click to decompress that file and extract all its files from the archive. (If double-clicking produces a
.tar file, double-click that file to finish the process.)
Now, you need to compile the C-language source code. Nearly every Linux, Unix, or BSD system includes
a C compiler. If not, download one for free from the GNU project at https://gcc.gnu.org/. If you're
on an Apple computer running macOS, you will probably need to download Xcode from Apple's App
Store.
To compile and link BRexx, open a terminal, change the directory to where you extracted BRexx, and issue
these three commands. You can issue all three using the root user id, or just the last command as root:
./configure
make
sudo make install
Now, just set the PATH and RXLIB variables as described previously.
If you receive the following error, you have downloaded a version of BRexx that requires that the MySQL
database be installed as a prerequisite. In this case, download and install MySQL from your favorite software
repository or the MySQL website at www.mysql.org. Then, rerun the above three commands.
Installing BRexx/370
BRexx/370 is a port of BRexx to mainframe operating systems running on the Hercules emulator, an
open source tool that enables you to run IBM mainframe OS's on other computers, such as personal
computers.
You can download BRexx/370 from at least three different GitHub projects. One implements BRexx for
CMS (VM/370). Rexx runs in CMS as memory-resident. Another project targets MVS (OS/VS 3.8). This
has been enhanced with support for VSAM/KSDS, 3270 formatted screens, TCP/IP, NJE38, and other
OS facilities. A third project called MVS 3.8 TK (for TurnKey) downloads both MVS and bundled
BRexx/370. This last project is easily installed via a Docker container.
Please see Appendix Z for more on BRexx/370, including product description, the websites for
downloading it, and install information.
362
BRexx
Installing on Windows
At various times, BRexx has offered automated installation for 32-bit Windows and Windows CE via
setup.exe files. The “Windows CE” section later in this chapter goes into more detail about installing
BRexx under Windows CE. At the time of writing, if you want to run BRexx on 64-bit Windows, you
would have to compile it from source code.
Three stack functions control the creation and destruction of stacks for the external data queue. All oper
ate in the manner expected, as per the discussion of how stacks work in Chapter 11:
BRexx was originally written for DOS-based personal computers. Given this heritage, it offers several
low-level PC-oriented functions that no other Rexx interpreter includes. These can be very useful for
PC control and diagnostic programs on older computers. Here is a brief list of these functions:
import(file | dynamic library) New in version 2.1, this function imports a shared
library using dynamic linking with Rexx routines. For
example, to access the MySQL API library under
Linux, code: call import “librxmysql.so” This
function effectively supercedes the load function.
363
Chapter 22
BRexx includes a full set of advanced mathematical functions. These are similar to those included in
other Rexx interpreters and operate in the manner one would expect:
acos(number) Arc-cosine
asin(number) Arc-sine
atan(number) Arc-tangent
cos(number) Cosine
cosh(number) Hyperbolic cosine
exp(number) Exponentiation
log(number) Natural log
log10(number) Logarithm of base 10
pow10(number) Power using base 10
sin(number) Sine
sinh(number) Hyperbolic sine
sqrt(number) Square root
tan(number) Tangent
tanh(number) Hyperbolic tangent
pow(a,b) Raise a to power b
Input/Output
BRexx supports the full set of ANSI-1996 input and output functions: charin, charout, chars, linein,
lineout, lines, and stream. Standard Rexx scripts that run with any other Rexx interpreter will run
under BRexx.
BRexx goes beyond standard Rexx I/O to give the developer alternatives. It offers a full set of C-lan-
guage built-in functions for I/O. These allow programmers to explicitly open or close files, open files
in specific modes by open and stream, flush file buffers, test for end of file through eof, read and
write either characters or lines, and position the file pointers by seek.
This C-oriented I/O model offers a more full-featured alternative to traditional Rexx stream I/O. Some
other Rexx interpreters offer such functions through stream function commands, and BRexx as well
includes an extensive set of over a dozen stream function commands.
364
BRexx
The C I/O model will be appreciated by those who require its powerful features or are familiar with the
C++ or C languages.
BRexx also provides a set of functions that interface to the free database, MySQL. These are built-in func
tions, not an external library. The MySQL functions provide a third alternative for I/O, one based on the
most popular open-sorce database. Here are these functions:
365
Chapter 22
Some recent BRexx releases include built-in support for the SQLite database. This is not an external
function library, but rather built-in support. These releases require installing SQLite in addition to BRexx.
To summarize, BRexx supports up to four alternative I/O methods. It always includes standard Rexx I/O
and built-in support for C-like I/O. In addition, various releases support MySQL database I/O and/or
SQLite database I/O. Each approach has its advantages. This chart contrasts them:
call load "ansi.r" /* loads the ANSI.R library functions for use */
Once loaded,all the functions in the library are accessible in the normal manner. The load function is
the rough equivalent of the rxfuncadd function used in other Rexx implementations, except that load
makes accessible the full set of functions residing in the library by a single statement.
In current releases of BRexx, the import function provides an alternative to load. This function
imports a shared library using dynamic linking with Rexx routines. For example, to access the MySQL
API library, code:
Here are some of the external function libraries and what they offer.
□ ANSIscre/o I/O (for Uaix cmODO—) — A Sft ofuboudo dozen ftincnionsthaampnipu Ithe the
cursor and control the attributes of text displayed on the screen. This provides s standard,
termicsi-icdepecdect iet of ANSI icreec coctroi routicei.
□ ANSI sSIeei IeO (foI Wiidows aid iOS) — The ANSI terminal emulation controls supplied as
built-in functions for higher performance.
366
BRexx
□ Coes/le (for )foeDO—) — A dozen funnnionathan control tho I/Oaeo me,maniauItte the corsor
and read keyboard input. These offer an alternative to ANSI Screen I/O modeled on the popular
C iangnage “fnncnie I/O’ (no “fnnin”) iiboaoy.
□ DaUnftmctions (for Xaix anODO—) — O dozen funCConsthet nonuan andmpnipul ate elates.
□ DOCfimctions DeD) .)—) -Tnccnhn funnnioneCheC retrlene low-levnlnnfoemationinalnging
ieg/enc, Offiec, and inceoonpc addoeiiei, diik and dioefcOoy infOo/aciOn, /afnine na/e and
more. These work with eny loom nl DOS bnt mey not be eveliebie under Windows (depending
On yOno WvndOei tloivOn).
□ CDCDICfuoctiono )fc Uaix nnODK— — 1'1.100^0^^^ convert ASCII EbCBCDPC avdvice
verie. Thii ii nielni befenie IBM end fompetibie meinlremei nie EBCDIC fherefter enfoding,
whiie eii midrenge end perionei fompnteri reiy on eSCSS. Theie lnnftioni prove very nielni in
dete treniler between mefhinei niing the two different dete enfoding ifhemei.
□ Filcfmcticns (for Uaix anODO—) — A dozen lik^' funcnionsthetacod er write an entire
fiie to/lrom e Rexx errey, retnrnfiie iize end ettribntei, end io on.
□ H MLi)CG.I'Cfi-pitrc:igiiicf^^mct)K — 'Henn' sup.orG CCfl pciipainn amdmanige cookies.
Windows CE
Brexx nei been ipefieiiy fnitomized to inpport tne Windowi oE lemiiy ol opereting iyitemi lor nend-
neid fompnteri. St infindei bniit-in lnnftioni lor Windowi oE nendneidi end en entometif initeiietion
progrem. St rnni netiveiy nnder Windowi oE (it doei not reqnire e DOS emnietor).
BRexx lor Windowi oE ii downioedebie in fompreiied binery lorm lor e veriety ol popnier nendneid
profeiiori, infinding MSPS, StrongeRM, end tne Hitefni SH* ieriei ol 32-bit profeiiori. Tne prodnft
infindei en entometif initeiietion progrem. To initeii BRexx on tne nendneid devife:
1. Downioed tne *.zip fiie fonteining tne bineriei lor yonr pertifnier nendneid end iti
opereting iyitem to yonr deiktop or ieptop Po rnnning Windowi.
2. Defompreii or nnzip tne downioeded fiie into en eppropriete loider on yonr Windowi Po.
3. Eninre tnet tne Windowi Po end tne nendneid ere fonnefted end tnet Mifroiolt eftiveSynf
or e iimiier prodnft ii eftive end fen trenilerfiiei between tne two mefninei.
4. Donbie-fiifk on tne fiie setup.exe.
5. BRexx ii entometifeiiy initeiied lrom tne Windowi Po to tne nendneid. Wnen tne initeiietion ii
fompieted, tne nendneid wiii diipiey e new deiktop ifon lor BRexxCE.
6. Seieft tne BRexxCE ifon on tne nendneid, end yon ere interefting witn BRexx.
Let’i briefly teke e iook et tne BRexx Windowi oE lnnftioni. Wniie more lnnftioni mey be edded end
tne workingi ol iome ol tne exiiting onei eitered, tnii iiit givei en idee ol wnet ii offered.
□CocCole)iIcft-ocC)— Tneie lnnftioni menege tne focCole)or diipiey. Tney neip yon menege
fnrior operetioni lor fnerefter-oriented inpnt end ontpnt:
□ clrscr() — oieeri mein window
367
Chapter 22
□ clreol() — Clears from the cursor position to the end of the line
□File system functions—These functions aid in using the file system. They support common file
operations and directory navigation:
□ copyfile(sourcefile,destfile) — Copies a file
□Windowing functions—The windowing functions set the window title, display message boxes,
and manage the clipboard:
□ windowtitle(title) — Sets the title of the window
□Unicode functions—The two unicode functions support two-way conversion between ASCII
codes and unicodes:
□ a2u(ascii_string) — Returns the ASCII string as Unicode
‘dir (STACK’
Access the command’s output by issuing pull instructions to read the stack’s contents. Chapter 4 pro
vided examples of how to write a do loop using the pull instruction to read lines from the stack. Use
that code to read lines output to the stack from commands like the preceding one when writing BRexx
scripts.
BRexx recognizes the keyword strings (stack , (fifo and (lifo , coded in the manner shown above.
These permit scripts to specify stack input/output and the ordering involved. Remember that FIFO
368
BRexx
stands for “first-in, first-out,” while LIFO specifies “last-in, first-out.” These terms were defined and
illustrated in the chapter on stack processing, Chapter 11. Please review that chapter if you need a
refresher on how the stack works.
Here’s a BRexx example of how to send data input into an operating system command by using the
stack. This sample command sends input to the operating system’s time command through the stack:
‘STACK> time’
ABRexx feature that extends beyond the Rexx standards is that commands and programs can be
invoked by coding them as if they were functions, so long as they use standard I/O. Here are examples:
say ‘dir’() /* displays a file list via the operating system DIR command */
say ‘cd’() /* displays the current working directory via the CD command */
directory = ‘cd’() /* capture results of the operating system’s CD command */
Be sure to encode the parentheses so that BRexx recognizes the “function call.” This technique can be
used with any command-line program (not just operating system commands), as long as they use stan
dard I/O. It is a convenient and powerful way to access outside services.
Windows users may see varied results when using the redirection techniques, depending on their ver
sion of Windows. This is because operating systems in the Windows family do not treat standard I/O
and redirection consistently. This is nota shortcoming of BRexx, but rather an aspect of how Windows
operating systems redirect I/O. Different Windows versions redirect I/O inconsistently.
BRexx recognizes several environments as the target for operating system commands: command, system,
dos, and int2e. int2e is the fast (but undocumented) way to issue command.com commands via
Interrupt 2E. This is a DOS-only feature.
/*********************************************************************/
/* FCOPY BREXX */
/* */
/* Uses BRexx I/O functions to copy a file. */
/*********************************************************************/
parse arg source target /* get filenames, retain lowercase */
369
Chapter 22
line = read(in_hnd)
do while eof(in_hnd) = 0
bytes_written = write(out_hnd,line,newline)
line = read(in_hnd)
end
exit 0
In the script, these lines open the input and output files and check for errors:
The open function follows C-language protocol for the mode, or the manner in which the file will be
used. These are the valid file mode flags that can be encoded:
r Read
w Write (overwrites any existing file)
a Append (adds to the end of any existing file)
+ Read and write
t Text mode
b Binary mode
The Text and Binary modes are mutually exclusive. Use either one or the other on any open statement.
The Text and Binary indicators are usually combined with one of the other flags. For example, you might
open a file for Read in Text Mode, or for Write in Binary Mode. Later in this discussion sample scripts
illustrate how this works.
The function returns a file handlethat can be referred to in subsequent file operations. If the function
returns -1, an error occurred during the open function.
370
BRexx
The do-while group uses the read function to read the inputfile:
line = read(in_hnd)
read returns either a specific number of characters or line(s). It can also operate on an entire file by using
the F parameter. In fact, we could have copied the entire file with one line of code instead of the do-
while loop:
The eof function returns 1 at the end of file, or 0 if the file is open and there are still lines to read. As
with the read and write functions, it takes thefile handle returned from the open function as a
parameter:
do while eof(in_hnd) = 0
The write function writes a line to the given file handle, optionally followed by a newline or line feed
character(s). It returns the number of bytes written:
bytes_written = write(out_hnd,line,newline)
The read function does not provide the line-end character(s) to the script, so the script must add the
new line to the output string through the write function newline parameter.
This code concludes the program. It closes both files based on their file handles, and checks for an error
during closing. The return code of 0 means the files closed correctly:
The C-like I/O model is powerful. The ability to work with files in binary modesimply by coding the b
option on the open function is especially useful. This allows byte-by-byte file processing regardless
of the manner in which the operating system separates lines or marks the end of file.
These file functions are all built-in. BRexx also provides an external function library for Unix and DOS
that can, among other features, read or write an entire file to/from an array. Finally, BRexx offers an
interface to the MySQL and/or SQLite open source databases as a set of built-in functions.
371
Chapter 22
BRexx supplies a core set of SQLite functions. Though few in number, they are all you need:
Function: Use:
SQL(sqlcmd) Executes a SQL query specified as a sqlcmd string. Returns the number
of rows
SQLBIND(col, "variable", value) Binds a parameter indexed by a ? at column position col by a value
SQLCLOSE() Closes the SQLite file connection
SQLGET([col[,V|T]]) Either returns the number of columns, a column's value, or its datatype
SQLERROR() Returns most recent error message
SQLITE() Initializes
SQLOPEN(database) Opens a database file
SQLRESET() Resets the current statement
SQLSTEP() Moves to the next row, returns "DONE" when at end
/* read records */
say "Record=" sql("select * from square")
do until sqlstep()=="DONE"
do i=1 to sqlget()
call write ,sqlget(i)"|"
end
say
end
call sqlclose
372
BRexx
To discuss this coding sample, you can see that the first three lines will be common to most
programs you write. They import (or load) the SQLite external function library, initialize SQLite for use,
and open the database file you want to work with:
call sql "create table square (id integer primary key, id2 integer, b blob)"
The next statement sets up -- or prepares -- a SQL insert statement. Notice how the three values
that will subsequently be placed into theinsertstatement are represented by question marks ( ?
). These are bind variables, variables that will be assigned values by a subsequent sqlbind
statement:
call sql "insert into square values (?,?,?)" /* prepare insert stmt */
Then, in the DO loop, you can see how the bind variables are substituted in by the sqlbind
statements:
The sqlstep function performs the actual insertion of the row into the database. Notice that a
sqlresetstatement starts every iteration through the DO loop to ensure that all SQL statements
are reset prior to their reuse.
After the table has been populated, the DO loop terminates. Now the program initiates another DO
loop, this time to read back all the information it has just inserted into the table.
This time the sql statement is used to read individual rows from the table. This demonstrates
how you can use the sql function to issue just about any SQL statement you want. In this
program, for example, it was used to execute CREATE TABLE, INSERT INTO, and SELECT sql
statements. The flexibility of the sqlfunction is what makes it possible to use BRexx for any
kind of SQLite processing you may want.
As before, the sqlstep command actually executes the sql SELECT * FROM statement built
previously.
Andfinally,sqlclose closes the database and terminates the program connection to it.
To summarize, the BRexx SQLite external function library provides a minimal subset of SQL
commands. But using them, you can perform any database function you need to. The sql
function is generalized to issue various database instructions.
In like manner, sqlstep is generalized for multiple uses. This program showed how it could be
used to issue both SQL INSERTs and SELECTs.
373
Chapter 22
This little collection of routines resides in the libraryfile namedfiles.r. It's only a set of seven
functions, yet it can manage most of your file I/O needs. Its distinguishing feature is that it enables you
to read an entire file into a Rexx array in a single statement. Conversely, you can write an entire table out
to afile with a single statement.
This single-statement "file read/write" feature is analogous to the mainframe Rexx EXECIO statement
(covered in Appendix N).
Function: Use:
BASENAME(file) Returns the filename without its the directory path
DIRNAME(file) Strips the the non directory suffix from file name
FILESIZE(file) Returns the file size in bytes
READWORD(file) Reads one word from a file
READSTEM(file,stem[,pool] Read the entirefileintostemusing as defaultpool0. Thestem.0
[,Upper]) contains the number of lines read.
WRITESTEM(file,stem[,pool] Writes to the file from stem, stem.0 contains number of lines to write
EXIST(file), STATE(file) Returns '1' iffileexists, else '0'
Recall from Chapter 4 how arrays (or tables) work in Rexx. They often referred to as compound variables
or stem variables because the symbols that represent them consist of two parts: astem and a tail (or suffix).
With this background, let's take a look at a simple program that demonstrates the use of this function
library and how to read and write an entire file in one statesent:
374
BRexx
/* This script demonstrates some BRexx file functions for Linux etc */
if \(exist(filename)) then
say 'Filename' filename 'does not exist!'
else do
The importfunction loads the external function library. parse reads a command line argument
containing the name of the file to work with.
The program verifies that the file name it's given exists with the exist function, nested inside the IF
statement test. Then, it uses the basename function to display the simple file name without its directory path,
and the filesize function to report the size of the file in bytes.
With housekeeping done, the program reads the entire file into the Rexx array named my_array.
Each input line from the file will occupy one slot in the target array. It's very important to code the
readstem function using the stem name of the table (which includes the period).
The readstem function always places the number of elements read into the 0 th array variable. In
other words, my_array.0 will contain the number of lines read.
The program then uses this information to process the array and displays its contents to the user.
Then the program invokes writestem to write the entire array to the specified file name. So it
creates a duplicatefile to the one the user supplied as a command line argument to the program. Be
sure the 0th element of the array indicates the number of elements it contains before the write.
A Linuxcat command displays this duplicate file to the user to confirm its validity, and the
program ends.
375
Chapter 22
It's important to distinguish between in file access methods between direct access to a specific record
within a file, versus positioning a file pointer forward in a sequential-accessfile. In the latter case, you
move a record pointer to a particular position within a file, then read that record. In many systems, that
means you have effectively read through all intervening records in the sequential file (even if they are
not passed to your program). And, you can't go backwards to a previous record.
A true direct or random access routine allows you to position the file's record pointer arbitrarily,
forwards or backwards. It doesn't read intervening records while seeking the one you specify.
Another distinction is that with some direct access routines, you must supply the byte position where
you want to read a record. Other systems perform that calculation for you. Instead you just supply the
relative record number within the file.
This example script prompts the user to enter a Pool Pass Number, then displays that patron’s record on
the screen. This sample interaction with the Pool Read program shows that the second record (Pool Pass
Number 2) is assigned to Ross Geller, and Pool Pass Number 4 is assigned to Joey Tribiani. Pool Pass
Number 17 is not yet assigned, because that relative record does not exist in the file. Pool Pass Numbers
correspond to direct access positions orslots within the random-accessfile:
Pass Number: 2
Person : Ross Geller
Phone : 476-1749
Pass Number: 4
Person : Joey Tribiani
Phone : 476-9876
376
BRexx
This script illustrates direct or random access to the pool pass records:
/**********************************************************************/
/* POOLREAD */
/* */
/* Reads random-access pool file records to display to the user. */
/**********************************************************************/
say ‘ ‘
call charout ,”Enter Pool Pass Number: “ /* get the user’s request */
pull pass_number .
else do
call seek in_hnd,position /* position to the record */
in_record = read(in_hnd,recsize) /* read user’s pool record */
say ‘ ‘
call charout ,”Enter Pool Pass Number: “ /* get user’s request */
pull pass_number .
end
The script opens the file for read access (r) and in binary mode (b). Binary mode is appropriate because
there are no line-termination characters within the file:
377
Chapter 22
This code uses the seek function to return the size of the file. The EOF parameter forces the file
pointer to the end of file. The calculation for input_limit is used to tell if the user has entered a Pool
Pass Number that does not yet exist (that is larger than the file size indicates has been stored):
The script prompts the user to enter a Pool Pass Number. This code takes that pass_number and calculates
the relative byte position where the record is located within the direct access file:
If the position is too big, the script knows that there is no such record and tells the user so:
If the Pool Pass Number is valid, the script uses the seek function to position the file position pointer to
read the proper record. Then the script reads that 30-byte record:
The parse instruction parses the record into its components, separated by commas:
Now the script displays the Pool Pass information on the screen. Then it prompts the user to input
another Pool Pass Number. When the user enters the string exit, the script terminates.
This script demonstrates how to use BRexx’s C-like I/O functions to open a direct-access file and
randomly retrieve records. It shows that advanced I/O functions can be used to implement different
approaches to data storage and retrieval. In this case, we stored fixed-length records within a standard
operating system file and retrieved specific records based on their relative record positions within the
file.
378
BRexx
The biggest competition to free DOS versions for embedded systems and device control programming
comes from "tiny" -- stripped down -- versions of Linux. Linux, too, is free, open source, and a rock solid
embedded platform. Linux offers many more capabilities than DOS. Yet DOS fulfills a need standard
Linux can not. DOS is a single-tasking, real-time operating system. Some use cases require that. And, of
course, DOS and DOS emulation are still used for gaming, supporting the estimated 7,000 DOS games
available for free download.
With its long-time support for DOS and its many special DOS functions, BRexx offers a Rexx
implementation specifically extended and customized for this world. This simple script demon
strates a few of BRexx’s DOS-specific functions. Here’s what the script does:
Here’s the script output. (We have removed extraneous blank lines from the output for readability.)
379
Chapter 22
Here is the program:
/**********************************************************************/
/* DOS INFO */
/* */
/* Illustrates some DOS-specific functions of BRexx. */
/**********************************************************************/
call load “dos.r” /* load the DOS function library */
call load “files.r” /* load the FILES function library */
else
say ‘File does not exist:’ testfile
380
BRexx
exit 0
The first statements in this program give the program access to all the functions in the two external func
tion libraries in the files named dos.r and files.r :
Loading the entire function library in one command is as convenient as one can possibly imagine.
Now the script issues a series of functions to retrieve and display information about the machine on
which it runs:
The storage function is particularly interesting. Without any operand, as in the preceding example, it
displays the total amount of machine memory. It can also be coded to display the memory contents at a
specific location. For example, this function displays 100 bytes of memory at machine location 500:
say storage(500,100)
The function can also be used to change that memory. This changes 5 bytes of memory starting at deci
mal location 500:
say storage(500,5,’aaaaa’)
Next the script prompts for the user to enter a filename. This code tests whether the file exists:
Assuming that it does, the script uses BRexx functions to display its size and attributes. The fileattr
function retrieves the file’s attributes, and the attr2str function converts them to a displayable charac
ter string:
381
Chapter 22
After displaying information about the file, the script reads the entire file into an array by this one
statement:
The script displays the contents of the file by reading through the array:
The zeroth item in the array ( filein.0 ) tells how many elements the readstem function placed into
the array. There is also a writestem function that corresponds to the readstem function. It writes an
entire array in a single statement. The element stem.0 tells writestem how many lines to write. Here
we’ve used a traditional do loop to display the contents of the array the script read in to the screen, but
we could also have encoded writestem to store the entire array to disk in a single statement.
Finally, the program demonstrates several different ways to issue operating system commands from
within BRexx scripts. In this case, the program issues the DOS ver ( version ) command. First the script
issues this operating system command in the traditional fashion:
‘ver’
The interpreter does not recognize this command, and so it sends it to the external environment for exe
cution. Of course, the default environment for command execution is the operating system.
Next, the script treats the OS command as if it were a function and captures and displays its output. This
technique works only if the command uses standard I/O:
Now the script issues the command again and captures its output into the stack:
To retrieve the results, just pull or parse pull the stack items. The queued function tells how many
lines are on the stack.
Finally, the script issues the ver command by the address instruction. First, it targets the system envi
ronment for command execution, then the command environment:
While this script demonstrates a small number of the BRexx external functions, it suggests how useful
they can be for OS-specific programming. The functions are easy to use, and the product documentation
clearly and succinctly describes how to code them.
382
BRexx
Summary
This chapter summarizes some of the extended features of BRexx. This goal is to give you a feel for the
features BRexx offers for Linux, Unix, BSD, macOS, Android, mainframes, Windows, DOS, and other
platforms. This is only a brief summary of what is available. Interested readers should download the
product and review its documentation for further information and product updates.
We demonstrated some of the extended features of BRexx in this chapter. These include additional built-
in functions, such as those for manipulating the stack, retrieving system information, higher
mathematics, C-language-style input/output, and MySQL and SQLite database access. We looked at how
BRexx runs in native mode under Windows CE and offers Rexx as an alternative scripting language for
handhelds that run that operating system. (Windows CE is a legacy system with license sales projected
to end in 2028.) Of course, the Android operating system dominates on cell phones and other handheld
devices, so chapter 25 covers BRexx on Android in some detail and provides programming examples.
We also discussed a few of the many function libraries that come with BRexx. These include libraries for
C-like console I/O, ANSI screen I/O, date functions, ASCII-to-EBCDIC conversion, file management, and
the like. While BRexx meets the TRL-2 standards, these additional functions give it the extra features
beyond the standard that developers often find useful.
Finally, the sample scripts in this chapter demonstrated several BRexx features. These included C-like
input/output, SQLite database access, array I/O, and direct data access. Direct or random file processing
is a useful tool that forms the basis for many kinds of applications. The final script illustrated a few of
BRexx’s extensions for DOS programming. While the average desktop user considers DOS a “dead
product,” DOS continues a subterranean existence in embedded programming and legacy gaming.
383
Reginald
Overview
Reginald Rexx was developed by Jeff Glatt of the United States. He took the Regina interpreter
and heavily modified it to customize it, and added features especially oriented to the Windows
operating system. The result is a well-documented product with features and tools that leverage
Windows. Reginald supplies all the Windows-oriented “power tools” and functions Windows
programmers expect. It’s a free product that provides an alternative to proprietary Microsoft
languages like Visual Basic and VBScript. As a Rexx interpreter, Reginald is standards-based and
free of charge.
While Reginald has its users, its website recently disappeared. So the product appears to lack
support at the time of writing. You might still want to take advantage of Reginald's large toolset,
which can easily integrate with other Rexx interpreters.
But be aware: the product documentation refers to some now-deprecated Windows technologies
(for example, ActiveX). We've retained such discussions in this chapter, in order to follow the
Reginald documentation -- without update or correction. Such material may be outdated for
current Windows programming.
This chapter provides an overview of the extended features and strengths of Reginald. The latter
half of the chapter offers several sample scripts that illustrate some of these features. We’ll start by
listing and discussing some of the advantages of Reginald as a Rexx interpreter. After describing
how to install the product, we discuss the extended functions of Reginald, how it supports
Windows GUI programming, its advanced I/O features, and its other extended features and
functions.
Adva ntages
As in previous chapters covering specific Rexx interpreters, we initiate the chapter by
discussing a few of the reasons you might choose Reginald. Here are some of the benefits to
Reginald:
Reginald automatically createc a Windowc file accociation between filec with the eItencion *.rex and
itc Script Launcher. Double-clicking on a *.rex file runc Reginald through itc Script Launcher. /t ic rec
ommended that, at a minimum, you inctall the Reginald interpreter, the REXX TeIt Editor, REXX
Dialog, and the online book LnarcoREXXoPrigraooncgonco56,479oEadyoSmnpd. Other toolc and add-onc may
be inctalled is you find a need sor them.
Tools
Reginald offerc a comprehencipe cet os toolc sor the ReII programmer running Windowc. Here are the
major onec:
□ Icdmallnr o— A Windowc inctaller sor the Reginald package. Among other seaturec it
automatically accociatec ReIIfilec with the interpreter co that you can cimply double-click on
any ReII ccript to run it.
386
Reginald
□ PtLaUnchnc—r — AGPapmdthat Pslps you easily rimlieginacd pcripts.ft also allows
you ts assign input arguments ts scripts aed ts create autsrue CDROMs with Roxx scripts.
□ AdministoationT—I — A singlc'-ponelgrogrammcr'sGUIthat sies ho tdo dooplonment and
debugging sf Rexx scripts. It allsls psu ts autslsad external functisn libraries and exit
haedoers fsr psur scripts, aed ts easiop set Rexx sptises, trace oeveos, aed script paths. This
tsso ceetraoizes admieistrative tasks aed is a seap ts oeare.
□ D ocumndtatiod — Expoaeatsrp dscumeetatise is a theme thrsughsut Regieaod. Fsr exampoe,
there are Wiedsls-stpoe heop spstems fsr the Script Lauecher, the Admieistrative Tsso, aed aoo
the sther csmpseeets. There are exteesive expoaeatises sf Regieaod scriptieg aed a fuoo set sf
leoo-dscumeeted sampoe scripts. There are severao seoiee bssks csetaieieg tutsriaos se
particuoar oaeguage features, aed evee a csmpoete seoiee bssk tutsriao se Regieaod. Everpthieg
psu eeed ts use Regieaod csmes lith the prsduct.
□ Rnxx inxt Editor (aka RnxxED) — Ae editsr specificaoop desigeed fsr lritieg aed testieg Rexx
scripts. The rsI tsso features csosr-csded spetax, a fuoo heop spstem, aed sther aids
specificaoop fsr Rexx prsgrammers. It aoss features a buiot-ie graphicao debugger ts debug psur
script bp settieg breakpsiets aed rueeieg sr steppieg thrsugh the actuao ssurce oiees ie the
text editsr liedsl. It aoosls psu ts add psur sle macrss lrittee ie REXX (lhich map use
add-ses such REXX Diaosg). Ysu cae therefsre add eel features aed ieterfaces ts the editsr.
□ R XX Dialog — Suppsrts Wiedsls rsI ieterfaces fsr Rexx scripts. Heops Rexx prsgrammers
deveosp csmpoete, tppicao-osskieg Wiedsls rsIs. The iecouded seoiee bssk fuoop
dscumeets the prscess aed makes it easp ts oeare hsl ts script Wiedsls rsIs.
□ ODBC drivnrs — Opee Database Cseeectivitp (ODBC) drivers fsr Regieaod access Micrsssft
Access Databases, dBASE fioes, sr Exceo fioes. These are fioes sf tppe *.mdb, *.dbf, aed *.xls,
respectiveop. The ODBC drivers aoss eeaboe oscao sr remste access ts a varietp sf database
maeagemeet spstems. These iecoude bsth spee ssurce databases such as MpSQL aed
nsstgreSQL, aed csmmerciao spstems such as SQL Server, Oracoe, aed Db2.
□ SQLitn drivnr — SQLite is a seof-csetaieed, embeddaboe, zers-csefiguratise SQL database
eegiee. It is usefuo as a oscao SQL-csmpoiaet database. Regieaod ieterfaces ts this spee ssurce
prsduct ts prsvide a fast, embedded database eegiee.
□ Spnnch fudctiod library — Ae extereao fuectise oibrarp that aoosls scripts ts use a
spethesized vsice ts prsesuece sr speak text. sses the ssued card sr speaker ts “poap”
the text. Audis sutput csmpoemeets the usuao screee sutput.
□ MIDI Rnxx fudctiod library — Ae extereao oibrarp that aoosls scripts ts read, lrite, poap, aed
recsrd MIDI fioes. MIDI fioes prsvide csmputerized csetrso sf musicao equipmeet aed are
aoss used ts poap music se the nC. The iecouded seoiee bssk iecoudes a fuoo tutsriao lith
sampoe scripts.
□ MIDI I/O fudctiod library — Ae extereao oibrarp that eeaboes ieput/sutput ts MIDI psrts. This
cseeects the nC ts eoectrseicaoop accessiboe musicao iestrumeets aed reoated devices.
□ Rnxx 2 Exn — This utioitp cseverts a fieished Regieaod script iets a seof-rueeieg *.exe fioe.
This aoosls distributise sf Regieaod prsgrams lithsut requirieg distributise sf the ssurce
script.
□ Math fudctiods— This oibrarp csetaies traesceedeetao mathematicao fuectises (e.g., cssiee,
siee, aed ss se).
□ RnxxUtil— Origieaoop devised bpIBM, these utioities maeipuoate stem variaboes aed prsvide
maep other service functions. This is the Wiedows-basod version of the IBM utility library
that is lideop distributed se maep sther poatfsrms.
387
Chapter 23
□ UegMexprxpiossz—st — This Iit^raron tonnsins Cunshohathat sarseueaularrepressions.
Regular expressionsare a precise way to describe string patterns. They can be very powerful
when applied to pattern and string logic.
□ Fhe FUNCDEFfeature — Allswr scriptk eoreeisaes andtlian e^^rriCly call any Cunchon in any
DLL, regardless on wnetner tnat DLL was writtes to se used nroo a Rexx sCripto
Masr on tne anove add-oss asd tools support tne Regisa isterpreter as well as RegisaldoTnese isClude
tne REXX Dialog, SpeeCn FusCtios linrarr, RexxUtil, Matn FusCtioss, Regular Expressioss, Rxoooo
Serial -dd-os, RxSoCs, MIDI Rexx, MIDI I/O, asd Rexx 2 Exeo
Windows GUI
Ose on tne oost ioportast Wisdows-speCifiC neatures on Regisald is tne anilitr to Create GUI dialogsnor
user isteraCtioso Regisald Calls tnis tneRCXXoNitcig, or RXNLG, neatureo REXX Dialog esanles Regisald
sCripts to Create asd Costrol tne Wisdows grapniCal user isternaCeo
Tne Regisald GUI nusCtioss are iopleoested as as extersal nusCtios linrarr is a DLL file Called
rxdlg.dllo OsCe REXX Dialog is isstalled, Regisald Cas tuticitdotnese nusCtioss so tnat tner are trass-
parestlr availanle to asr Regisald sCript rou writeoRegisald’s edmisistittiisoTiicooases tnis easro
Tne REXX Dialog add-os will also wors witn tne Regisa isterpreter, nut it offers additiosal error-nasdlisg
neatures usder Regisald, as well as tne anilitr to ne autoloadedo
-ltersativelr, rou Cas oasuallr isitialize (or “register”) Rexx extersal nusCtiossnor use nroo witnis asr
sCript tnat eoplors tneoo Do tnis nr Codisg tne nusCtios rxfuncadd to register ose extersal nusCtios
nor useo Better ret, Code tne speCial Regisald nusCtios rxdlgloadfuncs to register tccotne REXX Dialog
nusCtioss is tne DLL witn a sisgle lise on Codeo oall nusCtios rxdlgdropfuncs to drop all tne nusCtioss
osCe tne sCript is dose usisg tneoo
-nter oasisg tne dialog oasageoest nusCtioss availanle to tne sCript, isvose nusCtios rxerr to estan-
lisn now REXX Dialog will report asr errorso
388
Reginald
Next, set values for various controls, graphical objects that appear inside a window displayed to the user.
Then invoke the rxcreate function to create and display the window that has those controls. Now the
script issues rxmsg call(s) that allow the user to interact with the window’s controls. With a rxmsg call,
the script waits while the user manipulates the controls. rxmsg awakens the script when the user takes
an action that needs to be handled by the script. The script then accesses information describing the user
interaction and responds to it.
A script may repeatedly call rxmsg to interact with the user until the user takes an action to end the
interaction. Or a script may invoke rxmsg only once, if the user interaction is a one-time event rather
than an extended interaction through the window.
With these capabilities, scripts interact with users through one or more windows and through simple or
extended interactions. Later in this chapter we present several scripts that show how to program basic
GUI interfaces with REXX Dialog.
All dialog functions provide a return code. Check it for failure and to respond to any errors. Rexx condi
tions such as syntax and halt can also trap errors. The rxerr function customizes how Reginald han
dles errors via automatic message boxes and other techniques. Sample scripts later in this chapter show
how rxerr displays a comprehensive set of error messages.
389
Chapter 23
REXX Dialog supports the windowing concepts needed to create a typical GUI. Among them are con
trols, window moving and resizing, menus, accelerator keys, online help, timeouts, child dialog scripts,
and window modal states.
REXX Dialog also supports a basic set of Windows controls. These include push, radio, and checkmark
buttons; entry, list, and drop boxes; tree, spin, slider and text controls; a group box; and a menubar.
REXX Dialog hosts Internet Explorer’s rendering engine to allow your script to easily display any
content that would appear upon a Web page, such as clickable Internet links, graphics, tables, scrolling
banners, and so on.
In summary, Reginald’s REXX Dialog package provides everything required to create professional
Windows user interfaces.
The full name of RxDlgIDE is the REXX Dialog IDE(or interactive development environment). It comes
with a dialog resource editor that manages graphical components and integrates with RexxEd.
Input/output
Reginald supports the standard Rexx streaming I/O model and all the standard functions ( charin,
charout, chars, linein, lineout, lines, and stream). Reginald also offers many additional built-in
functions and features pertaining to the Windows file system. These include opening a file in shared mode
(i.e., allowing more than one program to access the one file simultaneously), creating and deleting directo
ries: deleting, renaming, movingfiles; obtaining a directory listing, resolving paths, reading and writing
numeric quantities from binary files; listing the drives and media on a system; and so on. Wildcard patterns
can be used as arguments to many of these functions so that entire groups of files are affected in one func
tion call (for example, to manipulate an entire directory of files with a single function call).
390
Reginald
For those accustomed to Windows programming, the power of the Reginald’s I/O functions should
readily be apparent. They provide the functionality necessary to create Windows applications. They put
Rexx scripting on competitive footing with any other approach to Windows programming.
Another alternative is to use Reginald’sOpen Database Connectivity, or ODBC,driversto write scripts that
connect to data sources such as Microsoft Access, Microsoft Excel, and Borland’s dBASE database.
ODBC also connects to commercial databases such as Oracle, DB2, and SQLServer, and to open-source
databases like MySQL and PostgreSQL.
391
Chapter 23
A third option is to use the SQLite interface. SQLite is an embedded, open-source, SQL desktop database.
SQLite is a good tool for scripts that require data management but do not need a large, multiuser
database. See www.sqlite.org for further information and downloads of SQLite.
□ LnamREPXgromramming in 56,479yaseSSeps
□ gromramminghvitE REXXDialog
□ USinm Reminold with o Coaaon Gotewoy Interfoce (CGI)
□ USinm Reminold to AcceSS the Internet
□ USinm MoilSlotS with Reminold
The pockome includeS Learn REXX Programming in 56,479 Easy Steps, on eoSy-to-reod tutoriol. ThiS online
book downloodS with Reminold, Self-inStollS, ond ploceS on icon on the WindowS deSktop for quick
occeSS. Throumh it, Reminold’S developer ShoreS hiS coaprehenSive knowledme of Rexx promroaainmin
the WindowS environaent.
Reminold includeS well-coaaented Soaple ScriptS for every one of itS odditionol feotureS. The kindS of
codinm techniqueS you con leorn froa thea include how to:
392
Reginald
Reginald provides the hooks into the Windows operating system that other Rexx interpreters lack. If you
need Windows-specific programming capabilities, Reginald provides them.
For example, this code sends a command to the cmd environment that invokes Microsoft Word:
Windows file associations are active. This makes it easy to start various Windows applications. For
example, to run a Windows Media Player on a *.mpg file, you could code:
‘c:\videoplayer\my_clip.mpg’
Similarly, you can run an audio clip by issuing a command string like this from the Rexx script:
‘c:\audioclips\my_audio_clip.wav’
‘webpage.html’
You can also start up Notepad by issuing a single statement. Start Notepad with an empty panel, or
place the user into editing a specific file by naming it on the statement, like this:
‘c:\windows\notepad.exe file_to_edit.txt’
Scripts gain complete power over Windows features when a knowledgeable Windows programmer
integrates commands into his or her scripts. Here we’ve shown how to access Windows applications
393
Chapter 23
such as Microsoft Word, the Windows Media Player, the Edge browser, and Notepad. The same
principles (and easy access) apply to any other Windows applications you want your Rexx scripts to
start, manage, and control.
It is important to note that some obsolete Windows operating systems always give return codes of 0, regardless
of whether the OS command succeeds or not. Error conditions FAILURE and ERROR are never raised.
This is not a flaw in Reginald but rather an aspect of the Windows operating system that the interpreter
does not control. This behavior has long since been corrected in current Windows versions but we
mention it for completeness.
Scripts may need to verify commands by some means other than just checking command return codes.
Chapter 14 demonstrates some techniques to use to accomplish this.
You can run a series of programs or files from one directory by using Reginald’s IterateExe function.
This function launches an external program a number of times. Employ a filename pattern to select
which files in a directory should run.
Reginald’s POpen function is an alternative way to issue shell commands. Command output goes into
the array specified on the POpen function, and variable position 0 (e.g., array.0) contains a count of
how many lines were output into the array. Process the array to process the lines output by the
command.
This sample code issues the operating system dir or directory command. The POpen function
directs output from this command to the dir_list array (which must be specified in quotes). If the
command return code were 0, the script would display the resulting directory listing:
The extra I/O functions in Reginald perform many common OS tasks. Use them to move, copy, and
delete files, get disk and OS information, and the like. Reginald reduces the number of OS commands
scripts need to issue and provides better control over them. Built-in functions are also faster than
sending commands to the operating system or using the POpen function.
Options
Reginald supports about two dozen Windows-oriented options for the Rexx options instruction. For
example, the LABELCHECK option causes a SYNTAX condition to be raised if the same label name is used
more than once in a script. This can help detect inadvertent errors due to cutting and pasting source code.
Another example is MSGBOX. Turned on by default, this option tells Reginald to display error messages
in a Windows message box, rather than a console or command-line window. Options like these enable
scripts to control Reginald’s behavior in the Windows environment.
394
Reginald
Windows Registry
Reginald scripts can query, read, write, create, and delete Windows registry keys and values. The Value
function provides this access. By default, registry operations apply to the Current User directory, but
they can also be applied to any other directory. Scripts read, write, create, or delete Registry files, and
create or delete Registry directories. Later in this chapter, we present a sample script that retrieves and
updates Registry information.
Error conditions
Reginald’s Raise instruction allows scripts to manually raise error conditions. Reginald’s user condition
allows scripts to define their own error conditions. This brings to Rexx a capability that many other pro
gramming languages support—the ability to define and raise your own exceptions.
This sample code enables a new error condition and then raises it:
my_error_routine:
The error condition is called user, and its error number is 1. Reginald supports up to 50 different user
conditions, numbered 1 through 50. Use the condition function to retrieve information about the error:
Windows DLLs
Windows external function libraries are typically implemented as DLLs. These are like the shared libraries
that provide external function libraries under Linux, Unix, or BSD operating systems.
395
Chapter 23
For DLLs that were specifically written to be used with Rexx, register and load them through the SAA-
compliant functions rxfuncadd and rxfuncquery. Or use Reginald’s Administration Tool to autoload
the external function library.
Reginald also allows script writers to access anyDLL function from their Rexx scripts, even if those DLLs
were not written with Rexx access in mind. This key feature extends to Reginald developers the full
range of Windows functionality. Register DLLs that were not specifically designed to be used with Rexx
through the funcdef function. This requires a bit more information on the call but gives access to any
Windows DLL.
Sorting
Reginald’s Sort statement sorts items within an array. The format is:
This statement sorts all items within the array stemname. The optional template is similar to the tem
plate used on a parse instruction. It controls matching string patterns, positional effects, and placehold
ers. The template allows for sophisticated sorts: by offset and length, on multiple values or criteria, in
descending order, with or without case-sensitivity, and utilizing search strings.
Multiple stacks
Reginald scripts can have multiple stacks, of which one is active at any one time. The rxqueue function
creates and deletes stacks. This function is also used to specify which stack is currently used. Reginald’s
Makebuf, Dropbuf, and Desbuf functions create, drop, and destroy all buffers within a stack. The
Queued function returns the number of items in the current data stack. The buftype function returns
information about the current stack for debugging purposes. One benefit to buffers is that you can easily
delete all items placed on them by a single function call to Dropbuf.
Parameter passing
Reginald includes the Use Arg function to provide more sophisticated forms of parameter-passing
between routines. This function makes it easier to pass multiple values between internal routines. It is
especially useful in returning large amounts of data to a caller.
do over loop
Reginald includes the do over loop to allow enumerating all the compound variables that use a given
stem. This is useful in processing all the variables that use a given stem name (even if you do not know
how many there are or what tail names they use).
Here’s an example. This code displays all the variable names used in the array named array.
do j over array.
say ‘array.’ || j
end
396
Reginald
array.1 = ‘ a’
array.2 = ‘b’
array.5 = ‘ c’
array.9 = ‘d ’
array.1
array.2
array.5
array.9
This helps you keep track of which array elements are used, especially when you’re working with a
sparse array(an array in which only certain slots or positions are used). do over does not guarantee any
particular order in enumerating the tails.
do over is useful in processing all elements in an array with a simple loop. This example sums all the
numeric values in an array:
do j over array.
array.j = array.j + 5
end
do over is very convenient for quick array processing. While syntax may differ, the do over concept is
implemented in several other Rexx interpreters including roo!, Open Object Rexx, and NetRrexx.
Array indexing
Reginald allows use of brackets to specify a compound variable as if it were a single tail name to be sub
stituted. For example, execute these two statements in sequence:
MyVar.i = 5
MyStem.[MyVar.i] ‘hi’
Reginald treats myvar.i (in mystem.[myvar.i]) as a single variable name whose value will be substi
tuted. Therefore, Reginald substitutes the value 5, and the variable name becomes mystem.5 after
substitution.
397
Chapter 23
Other functions
Reginald includes many built-in functions beyond the ANSI standards. In addition to the extra I/O func
tions mentioned previously, there are many other functions to retrieve system information, access exter
nal libraries, perform bit manipulation, and perform other activities. Let’s briefly take a look at these
functions and what they have to offer.
□ getpid nReturns the process ID (PID) of the process that launched the script
□ unixerror nReturns the error message for an operating-system specific error number
□ rxfuncerrmsg nReturn the most recent error message from rxfuncadd or funcdef
calls
□ Stunfunctions—Thiogpoupen funnaionsis similar to thosa aabii lahe iniyany oihet Rexxmter-
preters. shey manipulate the etternal data queue, or stack, as described in Chapter 11:
□ buftype nPrints debugging info about the stack
398
Reginald
Leveraging Reginald
Before we discuss some sample Reginald scripts, let’s ensure that you are familiar with and know how
to leverage Reginald’s development toolset. These GUI tools make scripting faster and easier. Here are a
few key tools:
□ Script Launcher—This GUI panel launches Rexx scripts. It presents a dialog panel that looks like
the standard Windows file-selection panel. Pass arguments to scripts through the Launcher, and
interact with scripts that issue say and pull instructions through the Launcher’s console window.
□ Administration Tool—This tool administers the scripting environment. Its GUI panel makes it
easy to set options and trace levels, set paths so that scripts can locate unqualified filenames,
and set values like numeric digits, fuzz, and form. It autoloadsexternal function libraries, so
the code that makes those libraries accessible does not have to appear in your scripts. It also
autoloads exit handlers, functions written in other languages that modify Reginald’s behavior.
Using an exit handler, for example, you could change the way the say instruction works so that
it pops up a message box instead of writing a line to the console.
□ Rexx Text Editor (aka RexxED)—This editor is designed for writing and testing Rexx scripts. Its
GUI features color-coded syntax, a full help system, a built-in macro language, and other fea
tures designed for Rexx scripting.
399
Chapter 23
□ RxDIgIDE — Tlnidindependyndlydepelt>pea cliaedg editor works in uonjunntK>n with XEXX
Dialog. It allows you to usd the mouse to graphically lay cut a dialog with its controls, and then
thd tccl idndratds a skdldtcn itXX ssript tc srdatd that dialci. It san run as a idxxtd masrc, sc
it appears under iexxtd’s macro menu and can output its skeleton script directly to a iexxtd
editor window for manual editing.
Ehis program retrieyes and displays information about the computer’s disk driyes, then lists specific
information about the C: driye. Here’s the script output:
/***************************************************************/
/* DRIVES INFO */
/* */
/* Illustrates a few Reginald drive information functions. */
/***************************************************************/
400
Reginald
say
feedback = DriveInfo(‘drive_info’)
if feedback <> 0 then say ‘Error on DriveInfo call’
else do
say ‘Information about your C: drive
say ‘Drive Type :’ drive_info.4
say ‘Serial Number :’ drive_info.2
say ‘Volume Label :’ drive_info.3
say ‘Size :’ drive_info.1
say ‘Bytes Free :’ drive_info.0
say ‘Filesystem :’ drive_info.5
say ‘Filename length :’ drive_info.6
say ‘Drive Flags :’ drive_info.7
say ‘ ‘
end
This script relies on two built-in functions to accomplish its work: drivemap to get information about
disk drives and driveinfo to get details about a specific drive.
The first block of code in the sample script issues the drivemap function to retrieve information about
the PC’s drives. The script omits the first parameter in calling drivemap, which specifies which drive to
start the query with. Leaving this out prompts drivemap to return a list of all drives matching the crite
ria. The second parameter is a keyword that specifies the kind of drives we’re querying for. The example
uses all possible keywords: fixed, cdrom, removable, ram, and network:
The second code block makes a single call to driveinfo. This function returns a several data items
about one particular drive. This encoding omits the drive name, so it defaults to the C: drive. The quoted
name drive_info is the array or stem variable that driveinfo populates:
feedback = DriveInfo(‘drive_info’)
The script displays the information returned in the array if this call succeeds and its return code was 0:
In this example, the user enters the filename of file_info_input.txt , and the script lists some basic
information about the file and displays the four lines that make up that file.
/***************************************************************/
/* FILE INFO */
/* */
/* Lists information about a file and displays its contents. */
/***************************************************************/
/* get the file name from the user, verify the file exists */
if state(filename) then do
say ‘File does not exist:’ filename
say ‘Press <ENTER> to end this program’ ; pull .
return 1
end
feedback = MatchName(,’FileInfo’,filename,,’NFSDA’)
if feedback = ‘‘ then do
402
Reginald
/* read and display all the file’s lines by the LOADTEXT function */
/* IN_LINES.0 will be set to the number of lines that are read */
say
say ‘Press <ENTER> to continue...’ ; pull .
exit 0
The script prompts the user to enter a filename. After he or she does so, the script ensures that the file
exists with this code:
if state(filename) then do
say ‘File does not exist:’ filename
say ‘Press <ENTER> to end this program’ ; pull .
return 1
end
The state built-in function returns 1 if the file does not exist and 0 if it does. The script simply displays
an error message and terminates if the file does not exist.
Next, the script uses the matchname built-in function to retrieve information about the file. matchname
is quite flexible. It can test for the existence of a file or directory, based on either attributes or a wildcard
file reference. It can also return information about the file, which is its use in the script. This shows one
possible use:
feedback = MatchName(,’FileInfo’,filename,,’NFSDA’)
The variable name, fileinfo, represents a stem or array that will be populated with the results of the
matchname call. filename is simply the input filename (as entered by the user), while the string nfsda
requests the information the script requires:
403
Chapter 23
So the string nfsda tells matchname to return the unqualified filename (nf), the file size (S), the date of
last modification (D), and the file attribute string (A).
matchname returns the null string if it succeeds. In this case, the script displays the file information:
if feedback = ‘’ then do
say ‘File Name :’ FileInfo
say ‘File Size :’ FileInfo.0
say ‘Last Modified:’ FileInfo.1
say ‘Attributes :’ FileInfo.2
say
end
else
say ‘Error on retrieving file info about:’ filename
matchname can do a lot more than shown here. You can specify file attributes as part of the file search
mask. You can even specify wildcard filenames. This is useful for listing (and processing) all the files in a
folder that match specified criteria. An upcoming example demonstrates this.
Finally, the script reads and displays the lines of the file. It reads the entire file into an array (a stem vari
able) in one statement by the loadtext function. It displays the entire file to the screen by a single call to
loadtext as well. Here is the code:
The first parameter to the loadtext function is the stem variable name. It must end with the period that
denotes an array name. The second parameter is the file to read or write. In the first call, this is file
name, the file to read. In the second invocation, it is not coded, which means to use the default device. In
that second call, the final parameter ( ‘S’ ) tells loadtext to save or write the data, rather than read it.
Since the call specifies the default device, this displays the lines in the array on the user’s screen.
This line shows that loadtext places the number of lines it reads into the 0th element of the array:
The ability to read or write an entire file in a single statement is very convenient. It demonstrates the
kind of power that Reginald functions add to standard Rexx.
Sample Scripts—GUIs
One of Reginald’s big advantages is its support for Windows GUI development. The REXX Dialogexter
nal function library supports this through its dozen or so functions. REXX Dialog supports all kinds of
Windows widgets including push, radio, and checkmark buttons; entry, list, text, and drop-down boxes;
trees, spin counters, and sliders; groups, menus, and HTML pages. We show only a few very elemental
examples here to demonstrate the basics of how to work with REXX Dialog.
404
Reginald
This first script displays a text entry box to the user. The user enters some text into this box, as shown in
Figure 23-1. Then the script reads and echoes this text to the user in a message box, as shown in Figure 23-2.
Figure 23-1
Figure 23-2
/***************************************************************/
/* DISPLAY INPUT */
/* */
/* Illustrates the basics of GUI interaction. */
/* Displays a text ENTRY box, writes back the user’s input by */
/* displaying it in a MESSAGE box. */
/***************************************************************/
signal on halt
signal on syntax
signal on error
call RxErr ‘ERROR|DISPLAY ’ /* displays error messages */
405
Chapter 23
RxVal.2 = ‘text’
do forever
/* display the text the user enters in the ENTRY text box */
end
The script issues several REXX Dialog functions, but it does not contain any code to access those external
functions! Normally, you’d expect to see code like this to load the external function library:
feedback = RxDlgLoadFuncs()
if feedback <> 0 then say ‘ERROR- Cannot load Rexx Dialog library!’
Actually, the script couldinclude this code to register and load the REXX Dialog external function library.
But we’ve chosen to use the Administration Tool toautoload the rxdlg DLL instead. Just start the
Administration Tool by double-clicking on it. The right-hand side of the panel allows you to autoload
function libraries. rxdlg.dll may already be listed as autoloaded. If it is not, just press the Add button
and add it to the list. Now, none of your scripts will need to include the code to register or load this
external function library. This eliminates repetitiously coding these lines at the start of every script. It is a
simpler approach, especially when a script accesses several external function libraries.
Because it does not include code to load the REXX Dialog external function library, the script starts by
enabling error trap routines. This line is of special interest:
406
Reginald
It is common to invoke rxerr at the start of a Reginald GUI program to establish error handling for the
script. This automates error handling very nicely, and replaces explicit code in the script to manage
errors. Figure 23-3 shows the kind of output rxerr displays when a programming error occurs. It is both
complete and automated.
Figure 23-3
REXX Dialog works with groups, sets of identical controlsor widgets. The major kinds of groups are listed
earlier (Push Button, Entry Box, and so on). To create a window, first assign values to variables that define
the appearance and operation of a group. This code, for example, sets the variables for the Menu group:
This code sets variables for the Entry group (the text entry box into which the user types the phrase the
program echoes):
The variables that must be set for each group are unique to the type of control. It’s not necessary to go
into them all here to understand the script. Reginald’s comprehensive documentation describes them all
and gives examples for each.
Every window must have a Window ID. Ours will be named rx. After all group variables have been set,
invoke the rxcreate function to display the window:
The first parameter is the Window ID, the second is the number of groups in this window, and the third
is the window title. noclose specifies that the script lets the user manually close the window, rather
than automatically closing it for him or her.
407
Chapter 23
With the window displayed to the user, the script enters a loop that repeats until the user manually
closes the window and terminates the program. These lines start that loop:
do forever
The call to the rxmsg function allows user interaction with the window. Here the call is simple. We just
want the script to display the window and wait for user interaction. rxmsg has several parameters for
more complicated interactions, for example, to manage interaction with multiple open windows or to
clear pending messages for windows.
Control returns to the script after the user takes some action upon the open window. REXX Dialog sets
variables so that the script can figure out what the user did: rxid and rxsubid. The simplified interac
tion in this script checks only for two values of rxid:
/* display the text the user enters in the ENTRY text box */
If rxid is the null string, the script knows the user clicked the close box in the window, so it exits.
If rxid is set to 2, the script knows the user entered a text string into the Entry box and pressed the
<enter> key. This is the case the script needs to respond to. As shown earlier, the response is to display
the text string the user entered in a message box. The rxsay function does this. Its first parameter,
text.1, is a variable set to the text string by the user interaction. Figure 23-2 shows that ok is the label of
the button the user presses to acknowledge the message box, and that the string You entered... is the
title of the message box.
When the user decides to exit, the script terminates by closing the main window:
To summarize, this simple example illustrates the basic logic many REXX Dialog scripts employ:
408
Reginald
More advanced scripts can employ more groups and controls and allow more sophisticated user interac
tion. This simple script shows how easy it is to get started with basic GUIs. Windows is a highly interac
tive environment. Along with Reginald’s extensive tools and functions, this means you can start
scripting Windows GUIs quite quickly.
Figure 23-4
Figure 23-5
/***************************************************************/
/* DISPLAY DIRECTORY FILES */
/* */
/* Displays all files in a directory. */
/***************************************************************/
409
Chapter 23
result = ‘’ ; list = ‘’
if list = ‘’ then
call RxSay ‘No files with this extension’, ‘OK’, ‘Warning!’
else do
The script starts by invoking two internal routines that (1) set up the error routines and invoke rxerr
and (2) initialize all the variables required for the text Entry box:
These two internal routines are not shown in the preceding listing because their code is exactly the same
as in the previous sample script. The line that displays the Entry box is also the same as that of the previ
ous script:
When the user enters a filename extension into the text Entry box, the script identifies this by the fact
that REXX Dialog sets the value of rxid to 2. The script enters a do loop that reads filenames with the
extension the user requested via the matchfile function:
410
Reginald
result = ‘’ ; list = ‘’
The matchname function should look familiar; the earlier sample script named File Info used it to
retrieve file information. In this case, no options are encoded for the matchname function, so it returns a
filename that matches the search parameter. The variable text.1 specifies the search parameter. In this
case, this search string includes a wildcard. This parameter is the input the user entered into the text
entry box. For example, the user might input:
*.txt
*.rex
As matchname retrieves the filenames that match the user’s wildcard pattern, the script concatenates
them into a list:
Each element in the list is separated by the vertical bar (|). If the list has no elements, no filenames matched
the user’s search criteria. In this case, the script displays a message box telling the user that no filenames
matched the pattern he or she entered:
if list = ‘’ then
call RxSay ‘No files with this extension’, ‘OK’, ‘Warning!’
If some files are found, the script displays them to the user in a text box. This code sets the necessary val
ues for the text box control:
Next, these two lines create the text box window, display it to the user, and control his or her interaction
with it:
When the user closes the text box, control returns to the script.
At this point, the user again sees the original text entry box. He or she can enter another wildcard filename
pattern, or terminate the program by closing the window.
411
Chapter 23
This simple program shows how you can integrate Reginald’s built-in functions for file management
with REXX Dialog GUI functions. Reginald provides a full set of GUI groups, controls, and techniques.
We have shown only the basics here. See the REXX Dialog online book that downloads with Reginald for
complete information and more scripting examples.
Since File contains the complete filename, this line sends the filename to the default command environ
ment (the operating system), and which executes the file. For example, if the user enters this into the
entry box, the program would execute all *.bat files found in the directory.
*.bat
Similarly, if the user enters the following, the program would send each file in the directory with the
*.wav extension to the operating system as a command.
*.wav
Windows recognizes the “Wave” file as an audio clip, and invokes the Windows Media Player to run it. In
this case, the program would run the Media Player for each Wave file in the directory. So, this program
could be used to play all the songs or audio clips in a directory, for example.
If you want your computer to talk to you, Reginald’s extensive sound libraries can do the job. The exter
nal Speech Function Libraryallows scripts to use a synthesized voice to pronounce or speak text. It uses
the sound card or speaker to “play” the text. Reginald’s MIDI Rexx Function Libraryenables scripts to
read, write, play, and record MIDI files. MIDI, or Musical Instrument Digital Interface, is a standard that
allows computerized connection and control of musical instruments. Reginald’s MIDI library enables
input/output to MIDI ports. Of course, MIDI files are often used just to play music on the PC. They are
another PC audio file format.
Let’s discuss the Speech Function Library. It allows you to encode text strings in scripts that the sound
card on the PC pronounces. What makes this interesting is that scripts can dynamically generate the text
strings the sound card speaks. This provides computer-synthesized voice without the need to record
audio clips in Wave files or other storage-consuming formats.
How could this be used? Since speech is a generalized computer/human interface, the potential is open-
ended. Use it as complementary output to typical GUI interfaces or to help the visually impaired. In one IT
project, the author used synthesized speech combined with a telephone autodialer to phone and read error
conditions to support staff on their cell phones. (Well, maybe this was not the best use of this feature...... )
To set up Rexx Speech, simply download and install it from the Reginald Web site. Use the Administration
Tool to add its DLL, named rxspeech.dll, to the list of autoloaded function libraries. You must also
ensure that your Windows PC has a SAPI-compliant speech engine installed. For Windows XP and newer
412
Reginald
versions, the operating system comes with this facility installed by default. For older Windows versions,
you may have to download and install the SAPI ActiveX control from Microsoft Corporation. The Rexx
Speech documentation has the link to the module you require, or just access www.microsoft.com and
search for the file named spchapi.exe or for the keywords sapi speech engine. Once you locate the
module, download it and double-click on it for automatic installation. Be sure that your PC’s speakers are
turned on and working before you verify the installation!
Using the Rexx Speech function library is easy. The library contains about 10 functions, and their use is
straightforward and well-documented in the online book that automatically downloads with the library.
/***************************************************************/
/* SPEECH */
/* */
/* Shows how to have your PC speak from text. */
/***************************************************************/
voice = SpeechOpen()
if voice == “” then do
say ‘ERROR- could not initialize speech device!’
return
end
exit 0
voice = SpeechOpen()
This initializes the default engine with the default voice and readies it for use. If it returns the null string,
it failed. Otherwise, it returns a voice parameter that is used during synthesis:
The speechspeak function should pronounce the string This is your computer talking. using the
voice designated by the voice parameter. If it fails, it returns an error message. Otherwise it returns the null
string.
413
Chapter 23
When the program is done using the speech engine, it closes it by the speechclose function:
That’s all there is to it. Let’s look at some other features of the Rexx Speech Library. The
speechvoicedlg function, for example, automatically pops up a list of voices from the speech engine,
from which the user can select the voice to use. You might invoke speechvoicedlg at the start of the
script to allow the user to select the voice the script employs:
id = SpeechVoiceDlg()
if id == “” then do
say ‘ERROR- could not get speech device ID!’
return
end
voice = SpeechOpen(id)
if voice == “” then do
say ‘ERROR- could not initialize speech device!’
return
end
In this case, speechvoicedlg returns a value that indicates which voice the user selected from the auto
matic dialog. Feeding this parameter into the speechopen call means that subsequent invocations of
speechspeak use this voice.
Prior to the calls to speechspeak that synthesize the voice, you may want to make calls to:
□ SpeechPitch — Setsthepitch
All three functions take the voice as their input parameter. The functions can be used to either set or
retrieve current settings.
GUI scripts that use REXX Dialog have the special feature that they can asynchronouslycontrol speech
synthesis. The reason is that scripts need to synchronize user actions with voice. Rexx Speech’s asyn
chronous speechcontrols ensure that what is spoken matches the GUI interaction.
MIDI Rexx
Now let’s discuss MIDI Rexx. This external function library allows scripts to create, edit, save, play, and
record MIDI files. This could either be used by musicians to integrate computers into their instrumenta
tion, or by the typical PC user to play music files.
To set up the PC environment, download and install the MIDI Rexx package from the Reginald Web site.
It consists of two self-extracting files: midifile.exe and genmidi.exe. At the end of the installation,
you will have these two new DLLs on your system:
414
Reginald
As always, we recommend using Reginald’s convenient autoload feature for access to the MIDI interface
from your scripts.
The typical MIDI script operates in this manner. First, it either loads an existing MIDI file into memory
(RAM), or it creates a new empty MIDI file in memory. Call the midiopenfile function once to establish
the existing or new MIDI file in memory. All subsequent MIDI operations apply to a file in memory.
MIDI files consist of tracksand events. Tracks are the larger entity, and multiple events occur within each
track. MIDI Rexx allows scripts to add, modify, and delete events within tracks, add or delete tracks, and
perform other kinds of processing.
When a script creates a new MIDI file, it adds tracks or events by invoking the midisetevent function.
If the script loads an existing MIDI file, it typically calls miditrack to set the search track. Then it
accesses individual events by calls to midigetevent. Scripts typically access one event at a time by
calls to midigetevent to perform their processing. midisetevent allows scripts to change aspects of
the currently selected event. When scripts are through processing a MIDI file, they save it to disk by the
midisavefile function.
In a manner very similar to the Rexx Speech Library, MIDI Rexx passes information to and from scripts
through a set of variables. The documentation clearly explains what variables are relevant to each MIDI
Rexx function and how they are used. Here’s a list of the MIDI Rexx functions:
415
Chapter 23
The Windows Registry is essential to Windows’ operation. Before running any program against it, be
aware that faulty updates can damage the operating system or even render it inoperable. Always back
up the Registry before working with it. To do this, select the Run... option from the Windows Start but
ton. Start one of the Registry editors, for example, by entering regedit as the command in the Run box.
Within the Registry Editor, select File | Export... and export the entire Registry to a file.
Review how your particular version of Windows backs up and recoveries the Registry and other vital
system information before running any test program against the Registry. Better yet, test any program
that interacts with the Registry on a test instance of Windows.
For maximum safety, programs that manipulate the Registry should always verify return codes. The
sample script omits error checking in the interest of brevity and clarity.
By default, Reginald functions work in the Current User section of the Registry
(hkey_current_user). After discussing the sample script, we’ll show how to access other portions of
the Registry.
The logic of the script is simple. It just performs one operation after another. It uses the value function
to read and change Registry values. Here is the script:
/***************************************************************/
/* TAKIN’ CHANCES */
/* */
/* Illustrates Reginald’s ability to work with the Registry. */
/* */
/* CAUTION- Backup Registry before running this program! */
/***************************************************************/
416
Reginald
In the script, these two lines create the directory, then the subdirectory:
The value call specifies the first parameter as a directory or subdirectory, which must end with the
backslash (\). The second parameter is omitted and the third parameter is the environment, which will
always be win32 when accessing the Registry. value returns a null string if successful. If the directory
already exists, no error occurs.
Next, this line creates a new file entry within the new directory and subdirectory. The first parameter
specifies the location and the second, the new value to insert. This script inserts a new file with the
value: test data only. If the file already exists, this action overwrites any previous entry:
Next, this line retrieves the new file value the script just inserted and displays it on to the user. Because
no second parameter or “new value” is specified, this value call retrieves information:
417
Chapter 23
At this point, the script pauses so that the user can verify the new Registry information via the Registry
Editor:
To verify the new Registry entries, just open a separate window while the script waits, and access the
Windows Registry Editor from within this new window. Now you can inspect the Register to see the
changes the script made. Assuming the new file value and its directory and subdirectory appear, the
user presses the <enter> key to continue processing. If there is a problem, he or she can press Ctrl-C to
abort the script.
Having made its Registry updates, the script cleans up after itself by deleting the information it added to
the Registry. Next, this line deletes the file entry. The absence of the first parameter specifies deletion. No
error occurs if there is no such file to delete:
Then the script deletes the directory and subdirectory it previously created:
The script ends by pausing so that the user can use the Registry Editor to verify that the Registry has
been properly cleaned up.
By default, Reginald functions work in the Current User section of the Registry
(hkey_current_user). To work in other areas of the Registry, just specify the location as the first part of
the directory name. This example retrieves a value from within hkey_local_machine:
To summarize, Reginald scripts can perform any desired Registry operation. Reginald provides a simple,
straightforward set of functions for this purpose. Back up your Registry before testing or running any
programs that alter it.
Summary
This chapter gives a quick summary of Reginald’s comprehensive Windows programming environment.
It delves into a bit of detail in only the areas of GUI management and file management. These epitomize
the OS-specific features Reginald offers Windows programmers.
Reginald distinguishes itself by its integration into Windows, its ability to program the Windows envi
ronment, and its comprehensive self-teaching documentation. Reginald presents an easy-to-use, yet
powerful, alternative to proprietary Windows-programming technologies.
This chapter also presented several sample scripts that demonstrate the extended features of Reginald
Rexx. They demonstrate several file functions, and also a bit about how to create GUI interfaces with
REXX Dialog. We looked into speech synthesis and the MIDI interface, and finally presented a script that
accessed and updated the Windows Registry.
418
Reginald
These sample scripts only suggest the wide-reaching functionality of the Reginald package. While
Reginald is powerful, its complete documentation makes it easy to get started. Windows developers are
urged to download and investigate the tools for themselves.
419
"5
Single Board Computers
by Howard Fosdick and Tony Dycks
Overview
One of the amazing features of Rexx is that it runs on computers ranging from the world’s largest
supercomputers down to the smallest devices. And it works very effectively on that wide range
of computers.
This chapter takes a look at Rexx scripting on the smallest computers: single board computers, or
SBCs. We’ll introduce the terminology of this world, and how programming projects must adapt to
the constraints it presents. Then we’ll talk specifically about how Rexx can be deployed to benefit.
We’ll give examples from experience about how to install and take advantage of Rexx, Open Object
Rexx (or ooRexx), and NetRexx on SBCs.
In the next chapter we’ll explore the related topic of how to run Rexx on cell phones and other
handheld devices. While programming smart phones differs from that of single board computers,
some of the hardware constraints you’ll face when working with those small devices are similar.
SBCs
Single board computers are complete computers built on a single circuit board. This includes a
microprocessor chip, memory, input/output devices, and the supporting circuitry needed to
create a fully functional computer. SBCs differ from desktops in that they don’t rely on expansion
slots, large cases, extensive cooling systems, or large-footprint disk drives. SBCs differ from
laptops in that they are not typically packaged with human interface devices like display screens,
keyboards, touchpads, or mice. SBCs strip a computer down to its essentials.
Chapter 24
SBCs often rely on SoC’s, or Systems On a Chip to achieve their density. SoC’s integrate most key computer
functions onto a single chip. This minimizes space, power, and cooling requirements - fundamental design
requirements for SBCs. SoC’s may include the processor, memory, input/output control, graphics processing,
cache memory, and other circuitry. Specialized SocC’s can include such additional features as
communications interfaces, radio modems, digital or analog signal processing, or other functions. It all
depends on the specific use for which the SoC is designed.
To summarize some of the differences between SBCs and traditional desktop and laptop computers:
□ Higher density of functional integration (based on higher levels of single-substrate circuitry integration)
□ ARM or other processors are common (rather than Intel or AMD CPUs)
□ Resources like processing power and memory may be lesser than desktops or laptops
□ May use SD (Secure Digital) cards for storage, rather than disk drives. Mini forms like SDHC / SDXC
/ SDUC are common, as are other form factors like micro-SD. Inexpensive eMMC flash storage is
also common.
□ Rarely bundles human-interface peripherals like displays, touchpads, keyboards, mice, etc
Uses
The allure of SBCs is that their small size, and correspondingly low power and cooling requirements,
mean that they fit an incredibly wide range of different applications. Educational use, home automation,
intelligent machines, media streaming, hobbyist experimentation, robotic control, factory automation, cell
phones, tablets, and the Internet of Things (IoT), all spring to mind as use cases.
In embedded computing, the SBC becomes part of a larger product. It may represent “the brains” or
intelligent controller for the machine in which it resides. Embedded systems often control real-time
operations and must be responsive to their specific domain. Factory robots offer an example.
Physical computing refers to how systems can sense and respond to the world in which they exist. Various
kinds of sensors enable the SBC to gather information about its environment, and then respond to changes
in that environment using the physical capabilities it controls.
In all these cases, the SBC is part of a dedicated system: a specialized computer designed and assigned to a
specific purpose. This is as opposed to a general purpose computer like your laptop or desktop.
422
Single Board Computers
The market for SBCs has exploded since 2012 when the first truly popular product came on the market - the
Raspberry Pi. In the past year alone, some 50 million Raspberry Pi’s were sold. Of course, the original
Pi has evolved into a family of SBCs with different sized boards and capabilities.
Dozens of companies now compete head-to-head with the Pi. As we transition into the Internet of Things,
the future appears bright for future growth of the SBC marketplace.
SBCs vary widely in capabilities, size, power, heat dissipation, and other features. To give you a more
concrete idea of functions, take a look at this typical board layout:
Power in
423
Chapter 24
Given that SBCs typically offer lesser processor and memory resources, it’s important to select a
lightweight operating system as host. Various forms of Linux are popular: they’re open source and
highly configurable, not to mention that they cost nothing up-front and are free of commercial license
entanglements. Many lightweight and ultra-light versions are available.
Graphical user interfaces are well-known as heavy resource consumers, so a lightweight GUI is also
needed. It may be desirable to run headless in some situations, that is, without a monitor. In other cases,
run with a line command interface. Next up the ladder in resource requirements are Windows Managers,
software that provides basic GUI functions without all the bells and whistles. And finally, there are
complete Desktop Environments: easiest to use and most convenient, but also most expensive in terms of
resource use.
In our projects, we found it necessary to test a number of Linux distributions and interfaces to
discover what worked best with our specific SBCs. We won’t get into that here, as what you’ll need
will be based on your specific SBC hardware and its architecture. Suffice it to say, if your SBC is not
certified for the operating system you select, some testing or experimentation may be necessary.
Rexx
Given its unique combination of programming simplicity with power, it’s not surprising that Rexx has a
long history with small devices. In the first edition of this book some years ago, we described how Rexx
worked with Pocket PCs, Windows CE, Windows Handheld, Windows Mobile, Palm OS, Nokia’s
Symbian OS, EPOC32, and EPOC. The next chapter tells how to run it on Android.
Rexx also works well with embedded DOS in all its forms, including DOS emulation. DOS versions like
FreeDOS, ROM-DOS, PTS-DOS, RxDOS, and REAL/32 continue to be popular for SBCs and embedded
systems, as do DOS emulators like DOSBox, Magic DosBox, and others.
□ Simple
□ Capable
□ Well standardized (with universal adherence to the TRL-2 and ANSI-1996 standards)
424
Single Board Computers
BSF4ooRexx
Java's popularity as a programming language has led to its becoming a standard interface language for all
sorts of platforms and projects. You can add full Java support and the Java libraries to ooRexx by installing a
tool called BSF4ooRexx. BSF4ooRexx stands for Bean Scripting Frameworkfor ooRexx. It provides a full-fledged
bridge between ooRexx and Java. With it, you can exploit the power of Java classes, methods, and other
features from within the easy-to-use ooRexx scripting language.
□ Camoufl geslJavalaslooRexxl(Javalappearsltolseltysamiclastlmessagelsaset)
□ Allsta ysu es ealape asme aapelea sf Java ysu migme lssaitet ittelevase is a eigmely-
lssetslletrastrlatefullyrptsgtammetrSBCresvitssmeser(eg, aetilereypisg,riearC/C++risflueslet
ayseax,reel)
Ofrlsutae,resruaerBSF4ssRexxrysurmuaerisaeallreme tequiaieerJavartuseimerasfetaterauppste.
NetRexx
Assemetrtayresrteapremersesefiearsf Javarastriearpstetrtmilertealizisgraraimpletrptsgtammisgriseetfale iar
esrisaeallrNeeRexx.
Like ssRexx, NeeRexx yielta all Java’a lapasilieiea tiemis eme lsseexe sf a aimplet lasguage. Bue emete ia ae
leaaersserimpsteasertiffetesle:rtmile ssRexxriararetueraupetaeersfrllaaailrRexx,rNeeRexxriarseaertealtisetr
aarar“Rexx-like”rlasguage.rIeriarsserayseax-lsmpaeislertiemretatieissalrRexx.
AartiemremerssRexx/BSF4ssRexxrlsmsisaeiss,rtiemrNeeRexxrysurseetresrisaeall emerauppsteisgrJavar
falilieiea. Cmapeet 30 gsea ises mste teeail assue NeeRexx, iea isaeallaeiss, uaea, ast saail ptsgtammisg
examplea.
425
Chapter 24
Pi Feasibility Projects
We conducted several projects using Rexx on the Pi. One project assessed and compared the viability
and performance of several Linux distributions that employ the RPM Package Manager (RPM). We
tested Red Hat, CentOS, openSUSE, Alma, and Oracle Linux.
A second project was to determine if we could install a full Linux distribution with a Windows Manager,
and then a full Rexx stack on top of that - all on a Raspberry Pi Zero or its equivalent, the lowest-end,
least expensive Pi. This SBC had only 1 gigabyte of memory. For this project, we used Debian Linux.
For the operating systems we installed the versions required for the ARM processors. We used tools like
Balena Etcher or the Raspberry Pi Imager to burn images to SD cards, and FileZilla for transferring files
from a Windows PC on the network to the Pi storage card. (Not all SBCs have internet connectivity.) We
used Apache’s Subversion utility to ensure correct package selection and installation.
□ NetRexx
□ Rett
□ Regina Rett
426
Single Board Computers
subversion is Apache’s Subversion software control system. cmake and g++ allow for compiling from
source code, and libncurses is the library we wanted to use for text-based terminal control.
You can substitute in the package manager for your platform if needed (eg, substitute yum or zypper or
dnf in place of apt).
Installing a JDK
Next, we decided to install and make available a Java Development Kit (JDK) as a prerequisite for ooRexx
with BSF4ooRexx and also for NetRexx. They minimally require a Java runtime environment.
Several different JDKs are available. Among them are those from Oracle Corporation, BellSoft, and the open
source product, OpenJDK. We selected the last of these three options.
Here’s an example of how to install OpenJDK using the RPM Package Manager yum command:
You can use the first command to search for the OpenJDK. The second line installs the OpenJDK.
The two export lines establish required environmental variables, $JAVA_HOME and a new search$PATH.
In Linux you can set these in the .bashrc login file for the particular login user id you use.
The final line simply double-checks the install version and verifies the environmental variables.
In some distributions, for example openSUSE, you can issue the exact same code, except that you can replace
the yum command with the zypper command.
Installing NetRexx
With the Java prerequisites in place, we move on to installing NetRexx. First, download NetRexx from any
of several websites, such as NetRexx.org or its project home at Sourceforge.net. We used
http://www.netrexx.org/downloads.nsp. Then we followed these steps.
cd /opt
sudo mkdir netrexx
427
Chapter 24
sudo cp $HOME/Downloads/NetRexx3.09GA.zip /opt/netrexx
cd /opt/netrexx
sudo unzip NetRexx3.09GA.zip
export PATH=$PATH:/opt/netrexx/bin
export CLASSPATH=$CLASSPATH:/opt/netrexx/NetRexxC.jar:.
Installing ooRexx
Next, we’ll install ooRexx. We do this by making a new directory for it calledoorexx and a subdirectory called
build.
cd $HOME
sudo mkdir oorexx
cd oorexx
sudo mkdir build
cd build
The svn checkout command brings the ooRexx code into the directory:oorexx-code-0
cmake sets up for the make and install commands, and we finish by verifying our work at the end by checking
the interpreter’s version.
Installing BSF4ooRexx
Find BSF4ooRexx at its Sourceforge home, at the URL sourceforge.net/projects/bsf4oorexx/.
Download the BSF4ooRexx install compressed file (its .zip file) into a directory of your choosing, and uncompress
it. The default installation will reside in /opt/BSF4ooRexx.
Change to the subdirectory holding theinstall script and run that script:
cd $HOME/bsf4oorexx/install/linux
sudo sh ./install.sh
Now copy the JAR file to the Java JRE Extensions Library and refresh the load library cache:
428
Single Board Computers
sudo cp bsf*.jar $JAVA_HOME/jre/lib/ext
sudo ldconfig
cd /opt/BSF4ooRexx
sh ./rexxj2.sh ./samples/classicRexxSamples/GetJavaSystemProperties.rxj
Now you have a fully functional Rexx stack with both ooRexx/BSF4ooRexx and NetRexx. You can
code Rexx or NetRexx with complete Java functionality available.
cd /usr/local
sudo cp $HOME/Downloads/regina-rexx-3.9.5.tar.gz .
sudo tar xvzf regina-rexx-3.9.5
cd regina-rexx-3.9.5
sudo ./configure -prefix=/usr
sudo make
sudo make install
To test Regina to ensure it’s installed properly, check the version number:regina -v
Here are the commands by which we installed Rexx/CURL from a source “tarball” on a
Debian system:
cd /usr/local
sudo apt install libcurl4-openssl-dev
sudo cp $HOME/Downloads/RexxCURL-2.1.0.tar.gz .
sudo tar xvzf RexxCURL-2.1.0.tar.gz
cd RexxCURL-2.1.0
sudo ./configure -prefix=/usr
sudo make
sudo make install
As always, we finish by testing for the product presence and its version: rexxcurl -v
While we installed Regina and Rexx/CURL from source code, you could alternatively install
from .deb packages (or .rpm or .apk files in other Linux distributions). It all depends on your
package manager.
429
Chapter 24
Practical Application
Marcel Dur of the Vienna University of Economics and Business wrote a thesis that demonstrates how
to control the hardware functions of a Raspberry Pi SBC by programming it in ooRexx with
BSF4ooRexx.
His programs manage the input/output of various Pi electrical pins that could be used to acquire
information through environmental sensors, or send out signals to control equipment functions. We’ll
walk through two of his example programs.
First, install ooRexx and BSF4ooRexx as previously shown. Then, you need to install software that
supports a software interface to the Pi’s hardware functions. You can pick any of several options:
Then, you must enable the Pi’s interfaces by this line command:sudo raspi-config
Let’s start with a very simple example program that turns an LED light off and on. It switches a
defined GPIO, called LED, between and high and low states in 1 second intervals. With apologies to
the author for redacting some of his nice documentation, here’s the code:
430
Single Board Computers
Let’s walk through this code. First, the script initializes by loading Java classes via BSF4ooRexx.
These classes reside in the Pi4J JAR archive:
This line identifies CPIO pin 29 for output, and assigns it to the default low state:
LED=gpio~provisionDigitalOutputPin (RaspiPin~GPIO_29,pinstate~low)
Here’s the driver. The last line is required to enable Java support via BSF4ooRexx:
Depending on how Pi is connected to other devices, you could use simple pin manipulation like this
to control many other devices and their functions.
Now, here’s the exact same program using WiringPi software support instead of PiJ4:
This version of the program is coded procedurally - instead of with objects -- by using address
system calls. As we previously stated, ooRexx supports either approach to programming. So you
have choice. In both cases, you still have access to all Java capabilities through BSF4ooRexx.
431
Chapter 24
Here’s a second example program from Mr Dur. The Raspberry Pi can control devices using its hardware PWM pin. This
program rotates the arm of a SG90 servomechanism up a maximum of +90 or -90 degrees, as based on the user’s input
parameter:
/*------------------------------------------------------------------ */
/* */
/* GPIO PWM */
/* with WiringPi support */
/* */
/* This example demonstrates the use of the PWM function of the */
/* Raspberry Pi. As an example, an SG90 servo is controlled with it. */
/* The servo can rotate approximately 180° */
/* */
/*------------------------------------------------------------------ */
-- Operating frequency of the servo is 50 Hz -> 20ms PWM Period
--set GPIO Pin 26 to PWM mode
address system "gpio mode 26 pwm"
address system "gpio pwm-ms" -- PWM mark space mode
address system "gpio pwmc 192" -- PWM clock divider
address system "gpio pwmr 2000" -- PWM range
do forever
say " Enter a value between 50 and 250 "
parse pull angle
if angle >49 & angle < 251 then do
command = "gpio pwm 26 " angle
address system command
end
else say "wrong input - must be between 50 and 250 "
end
exit
The lines that start with address system perform the setup required for an SG90 servo. These details are as
documented in its specifications and those of the attached Raspberry Pi.
Then the do loop reads a user input and translates it into thegpiocommand to manipulate the servo.
This program demonstrates you can drive SBC hardware to monitor and control attached devices through ooRexx with
BSF4ooRexx, instead of using harder-to-code Java.
432
Single Board Computers
Summary
With an exploding population of single board computers and the rise of the Internet of Things, the need for
simple ways to program all these devices becomes critical.
Rexx offers a simple, proven solution. It’s small size, standardization, stability, portability, and
power all render it suitable for addressing this need. It’s also open source and freely available.
Classic Rexx is one option. It’s simple and interfaces to hundreds of free and open source tools.
ooRexx expands upon this base by adding object-oriented capabilities. In conjunction with
BSF4ooRexx, it opens up the full power of Java and its libraries to the simple syntax of Rexx. NetRexx,
too, enables programmers to climb opon Java’s capable shoulders while enabling simpler coding
solutions.
In the first projects we described, we presented line commands that set up a full Rexx stack including
classic Rexx, Object Rexx, and NetRexx. The latter two were armed with full Java capabilities.
Whatever Java can do in their environment, they can do.
The last two example programs showed how easy it can be to control low-level Pi functions with the
proper software stack. ooRexx with BSF4ooRexx is an essential piece of the puzzle: it helps you
perform any function Java can with the Pi while presenting a simpler, cleaner interface.
1. What are key differences between single board computers, and desktops and laptops. How do
these differences affect your coding solutions?
2. Why are so many SBCs sold without cases?
3. Among classic Rexx, ooRexx, and NetRexx, which require Java support as their prerequisite?
What JDK’s are available?
4. What is Apache Subversion? Why might you use it?
5. What is BSF4ooRexx and how is it used? Do you need to install it to code Java Beans in NetRexx?
6. Say you’re a system administrator who codes procedurally in Bash and Rexx. Do you need
to learn object-oriented programming to code with ooRexx?
7. You want to program a Pi to control a robot. How would you manage access to the pins to
control the necessary hardware functions?
433
Rexx on Android
Overview
As discussed in the previous chapter, Rexx has a long history of running on handhelds and cell
phones. Rexx ran on now-obsolete devices like Palm Pilots, Nokia phones, and Pocket PCs.
Rexx continues its strengths on small devices today in its use on Android cell phones and
tablets. Android is by far the most popular operating system in the world for smartphones,
and it has been for many years. It’s also widely used for tablets and specialty handhelds.
Apple’s iOS is its only rival. Together, the two products wield a duopoly over phones and
tablets.
Android is developed by Google and the Open Handset Alliance. Most of Android is free and
open source software, though the Google version does include some proprietary code. Android
is based on a modified Linux kernel at its core. It then layers on much Android-unique code,
including mobile applications, its own Application Programming Interface (or API), and
middleware.
This chapter explores three different ways to run Rexx on Android. Each has its benefits and
limitations. We’ll enumerate the kinds of tasks you can achieve by programming your Android,
and walk you through some coding examples. By the end of this chapter, you’ll be able to
decide whether you’re interested in further exploring Android scripting, and you’ll know how
to go about it.
Three Options
There are at least three Rexx interpreters available on Android. We’ll discuss each in its own
section:
□ BRexx
□ Rfor fon AoAoad Oaloo known as "Rexxoid")
BRexx
We discussed BRexx in chapter 22, and explored its utility on small devices. Now we’ll discuss its use
specifically with Android.
To run BRexx on Android, you need a software interface called theScripting Layer for Android(or SL4A). SL4A
allows developers to automate Android tasks in scripting languages instead of using Java. One of those
scripting languages is Rexx. The advantage, of course, is that Rexx has simpler syntax than Java. Also, it’s
much faster and easier to develop with an interpreted scripting language than a compiled language like Java.
SL4A is a scripting host. It makes the Android API available to scripts through various interfaces called facades,
designed to make programming easier. Some of the available functions are those for the camera, location,
media recorder, media player, battery manager, and more. BRexx gives you a simple way to tap into this.
Since SL4A is a prerequisite, you need to download and install it first. It is hosted at GitHub as project sl4a.
Note that there have been questions about this project’s continuation, so you may need to download a fork
instead. In this case, just search for “SL4A” at the GitHub repository.
After installing these two items, start up SL4A, then navigate to the Android folder. You’ll find a list of Rexx
scripts, all with BRexx’s traditionalfile suffix of.r. From there you can run, edit, save, or delete scripts.
Let’s take a look at a few sample programs. Here’s the traditional “Hello World” script:
The first two lines start every BRexx Android script. They perform the necessary initialization of the
environment. They load the BRexx Android functions and establish communications with SL4A.
The last two lines set up and send the message to the screen. In Android, a toast is message that appears for a
few seconds on the screen without disturbing the user, and then disappears. The call tomakeToastvia the
Android API sends the toast to the user and makes the message appear on his screen.
436
Rexx on Android
How about we take a photograph? Here’s an example script distributed with BRexx for the task. The
cameraCapturePicturefacade places the image in the filename specified by its operand:
Or we could engage the user in an echoing dialog. The dialogGetInput call prompts the user for
input and receives it, while thettsSpeakfacade speaks its operand to the user:
Here’s a more complete example. It was written by Eva Gerger of the Vienna University of Economics and
Business in her thesis on Android programming with BRexx.
The program retrieves the status of the battery and displays it to the user as a percentage.
In this script, the batteryStartMonitoring call initiates battery monitoring. Monitoring takes time
to develop results, so eventWaitFor waits of up to 5000 milliseconds for a battery event to transpire.
Eventually the script captures the battery level via batteryGetLevel. Then it stops battery
monitoring with batteryStopMonitoring, and displays the result via makeToast.
Here’s another illustrative script by Eva Gerger. This one will send SMS text messages to the number of
individuals the user specifies in response to the initial dialogGetInput prompt.
437
Chapter 25
The do loop is executed for as many times as the user entered. Inside the loop, the dialogGetInput function
prompts the user to enter a phone number. Then, the smsSend call sends a text to the entered phone number
stating that "You are number" followed by an ascending integer.
Location Example
Ever wonder how your phone spies on you? This example program distributed with BRexx shows how
easy it is to track your location:
The getLastKnownLocation function estimates where you are. Then json swings into action with
geographical positioning (gps) and eventually finds your longitude and latitude. This ultimately allows the
program to display your network location, longitude, latitude, and your postal or "zip" code.
Of course, if you want to precisely and accurately pinpoint someone’s location, there’s a bit more to it than
just this. But this simple program gives you a good idea of just how easy this is to program in a high-level
scripting language.
438
Rexx on Android
Bluetooth Example
For the final example, we’ll see how to implement Bluetooth communications between two Android cell phones.
Bluetooth is a short-range wireless communications protocol that can be used to exchange data between devices.
These two programs were written by Eva Gerger. The screen shots below are from her presentation on her code
at the Rexx Language Association annual symposium.
The “server” script must be started prior to running the “client” script.
These photos show their interaction. The server starts up and asks for connection permission. The user sends a
message to the client: “Hello, over there!”. As you can see, the client receives that message:
/* Server */
call import "android.r"
call AndroidInit
call toggleBluetoothState true
say "bluetooth is on!"
call bluetoothMakeDiscoverable 300
say "now discoverable"
call bluetoothAccept "457807c0-4897-11df-9879-0800200c9a66", 0
439
Chapter 25
say "connected!"
message = dialogGetInput("Your Message","Please enter message:")
call bluetoothWrite message
call sleep 10
The bluetoothAccept function looks for a Bluetooth connection. Note that the client has to use the same
identity number as given in this call.
Then the dialogGetInput call prompts the user to enter his message. The bluetoothWrite message
call sends it to the connected client.
/* Client */
call import "android.r"
call AndroidInit
call toggleBluetoothState true
a = bluetoothConnect("457807c0-4897-11df-9879-0800200c9a66 ”)
say a
call sleep 5
a = bluetoothRead(4096)
say a
pull .
After initialization, just as in the server program, the toggleBluetoothState and bluetoothConnect calls
enable Bluetooth and look for a connection matching the specified ID.
The bluetoothRead function accepts data through the live connection, and the say statement displays it on
the client device.
440
Rexx on Android
Rexxoid is based on Mr Richard’s C++ Rexx library. This code runs on most platforms, and was initially deployed
on Palm OS. Richard updated it to Android and today it offers a way to code Rexx scripts that issue Android
shell commands.
You can download and install Rexxoid from the Google Play Store. In its GitHub home, at
https://github.com/Jaxo/yaxx, you’ll find a half dozen sample programs as well as screen shots of the
product in action.
As is standard for Android apps, Rexxoid has a Manifest file that enumerates its permissions. If you encounter
any errors requiring further permissions, this is where you can add them.
When doing such work, it may be helpful to connect the phone to a laptop or desktop computer via USB cable.
This also facilitates debugging.
Rexxoid has a fundamentally different design than BRexx for Android. While BRexx uses the SL4A scripting
interface, Rexxoid is primarily intended to issue shell commands. From this perspective, its functionality is a bit
more limited.
Example Scripts
Here are a few code examples from a Rexx Language Association symposium presentation by Julian
Reindorf using Rexxoid. The code is all by Mr Reindorf. Some lines wrap around due to screen wrap and
typesetting restrictions, so we’ve included line numbers for clarity:
441
Chapter 25
Creating emails:
1 text = ""
2 DO i=1 TO 100
3 text = text i
4 END
5 "am start -a android.intent.action.SEND --user 0 -t 'text/plain' -e to
'[email protected]' -e android.intent.extra.SUBJECT '1 to 100!' -e
android.intent.extra.TEXT '"text"'"
Setting alerts:
1 DO 5
2 "input keyevent 24"
3 "sleep 0.6"
4 END
5 DO 5
6 "input keyevent 25"
7 "sleep 0.6"
8 END
442
Rexx on Android
At the time of writing, scripts can only be run on a rooted device. They also require the Android Software
Development Kit (SDK). You may have to update the Manifestsfile to get them working.
ple Scripts
All example programs were written by Mr Grundmann-Kahr. Note that a few lines wrap around due to
typesetting limitations. We have reproduced them without introducing any characters indicating line
wrapping.
First up, a simple script to display memory usage. This shows how you send commands to Android via the
ADDRESS SYSTEM instruction:
Say hello to your friends in Germany with an email (and impress them with your fluent German!):
443
Chapter 25
DO FOREVER
SAY "Which URL would you like to visit? (Type 'exit' to end the session)"
PARSE PULL url
SAY "Do you want to save the URL to a Text File for later use? (Saved in favorites.txt) (yes/no)"
PARSE PULL saveOption
This last example shows how to create an alert. (Note that typesetting wrapped around the line containing the am
command):
SAY "Done!"
444
Rexx on Android
Rexx on Apple
What about running Rexx on Apple products like the iPhone and iPad? Regina Rexx runs under the
iSH app, downloadable from Apple's App Store. iSH runs a Linux shell environment locally on your
iOS device, using a usermode x86 emulator. You would download Regina's 32-bit Alpine Linux
package for this purpose.
Summary
Rexx has long run on a wide variety of cell phone, tablet, and specialty handheld operating systems.
In this chapter we looked specifically at three Rexx interpreters for Android. BRexx, Rexxoid, and
ooRexx for Android are the current offerings. All three are free and open source. We presented several
programming examples with each to show what you can do with them, and how you would code.
If you use Rexx on any other platform, your knowledge of the language transfers to the Android
without change. All you have to learn are Android-specific behaviors, calls, functions, and the like.
445
r4 and Object-Oriented roo
Overview
Kilowatt Software offers two free Rexx interpreters. r4 is a classic Rexx interpreter that meets the
TRL-2 standard and is at language level 4.00. roo! is an object-oriented superset of r4. It offers a
complete object-oriented Rexx programming language. Both r4 and roo! run under all versions of
Windows and are complemented by many add-on tools for the Windows environment.
This chapter briefly overviews both.
We’ll discuss the advantages to r4 and roo! as a pair of Rexx interpreters from the same company.
After telling where to download and install the product, we’ll describe some of the tools that
Kilowatt Software provides for both r4 and roo! These help developers leverage Windows features
with much less effort than would otherwise be required.
The heart of the chapter is the quick overview of roo!’s object-oriented scripting features. Object-
oriented programming is an approach many feel dramatically raises developer productivity. roo!
supports all object-oriented features, while retaining full compatibility with standard Rexx. We’ll
describe the additions roo! makes to classic Rexx and explain how they support object-based
scripting.
Before we start, an important note: Kilowatt Software took down their website and the company
appears to have gone out of business. This leaves their Rexx products unsupported. This chapter
discusses r4 and roo! as per their last available documentation. Such material may be outdated for
current Windows programming.
Chapter 26
Advantages
r4 and roo! are two different products from the same vendor. Nevertheless, they share many of the same tools and
characteristics. Here are some key features of r4 and roo!:
□ Windows-oriented -- The products are customized for Windows, with easy installation and
developer tools specifically designed for Windows. Roo! Is a free object-oriented Rexx that is
tailored and configured for Windows.
□ Introductory material -- The products feature several tutorials and presentations on classic Rexx
and roo! The r4 and roo! Documentation is easy to read and informal. Beginners can learn the
languages quickly, while experienced developers become immediately productive.
□ Sample scripts — r4 and roo! each ship with about 50 sample scripts. The examples perform use
ful, real-world tasks such as HTML processing, managing sockets, processing comma-separated
value (CSV) files, statistical functions, file conversions, and the like. These scripts can be used as
models or a starting point for your own.
□ Upward Compatibility — The products were developed by the same company, so the object-oriented
roo! is fully upward compatible with the classic procedural r4.
□ Tools — r4 and roo! come with a number of useful developer utilities. We describe them later.
□ Windows GUI development — Windows GUI tools often include hundreds of functions and
dozens of widgets with hundreds of attributes. This provides flexibility but confronts
developers with a steep learning curve. For beginners it can be downright bewildering.
Kilowatt Software offers a smaller, more focused tool set that makes Windows GUI
development a snap.
□ Object-oriented migration — r4 is 100 percent upwardly compatible with the object-oriented roo!
You can ease into object-oriented programming with roo! while maintaining backward
compatibility with your existing classic Rexx scripts. Whether the legacy scripts were written
for r4 or any other classic Rexx interpreter, as long as they stay within the Rexx standards, they
will run under roo!
□ The OOP alternative — roo! presents an object-oriented Rexx alternative to the Open Object
Rexx interpreter (developed by IBM Corporation and today enhanced and maintained by
the Rexx Language Association). roo! features completely different design and class
libraries. Windows developers have a choice of two different object-oriented Rexx
interpreters for their platform.
448
r4 and Object-Oriented roo!
Verification
Verify that the install succeeded by running a few of the sample scripts in the product directory. For
starters, run the scripts from the Windows command line. Just change to the product directory and enter
the interpreter followed by the script name:
or
Environmental variables can be set to customize program execution. All are described in the product
documentation. You will also want to add the r4 and roo! installation directories to your path variable.
Documentation
After installation, read the readme.txt file. This contains the licensing terms. r4 and roo! are free for
both personal use and corporate customers and come with a limited warranty.
A number of *.htm files in the product directories contain the documentation. Just double-click on any
file to read it. The r4 documentation includes these two key documents:
Document File
Document File
There are also *.htm files that describe each of the tools. For example, to learn about some of the GUI
accessories, just click on any of the files filedlg.htm, msgbox.htm, prompt.htm, or picklist.htm.
Each contains an explanation of the associated command and a complete sample script. Any of the
add-in tools you install, such as AuroraWare!, Poof!, and Revu follow the same approach. An *.htm file
explains each command. Let’s discuss these tools now.
449
Chapter 26
Tools
Kilowatt Software supplies a full set of Windows-oriented tools to complement r4 and roo! Figure 26-1
pictorially summarizes them.
Figure 26-1
450
r4 and Object-Oriented roo!
T-.s. eUI tccls ar. mcr. r.adily l.arf.d t-af mcr. ccmpr.-.fsiv. but ccmpl.x eUI d.v.lcp-
ment tccls. They transfer the Rexx eose-cf-use philosophy tc the world cf Windows GUI
d.v.lcpm.fts I— af advafc.d eUI ift.r—ac. isr.quir.d, rcc! afd Java caf us.d tcg.t-.r tc
creote it.
□XMLGenie!— A utility thot outcmoticolly ccnverts XML tc HTML.
□Poof! — This prcvides mcre thon 135 Windcws ccmmond-line tccls. Scme cf the oreos they
ccver include:
□ Botch-scripting oids
□ Binory file utilities
□ Clipbcord utilities
□ Ccmmond lounching
□ Tosk ccntrcl
□ File monogement (fcr mony different file fcrmots)
□ HTML preporoticn
□ Mothemoticol ond ccnversicn rcutines
□ Scftwore develcpment
□ File oids
□ Mony miscellonecus utilities
□ Revu—Cclcrful text viewer. Highlights text opprcpriotely fcr different prcgromming
longuoges.
□ Chill—This utility ccnverts on r4 cr rcc! prcgrom intc on unreodoble, clcsed-scurce file. This
ollcws ycu tc give cthers the use cf ycur scripts withcut expcsing the scurce ccde.
□ EXE Convorsion Lit—T — Th inconvertsstripls iosd- nC-alWeWindowsexocutables, *.exe
files.
□ CFLOW—C/C++ flcw onolyzer.
□ Vornicalsapplicaniors—Kilcwott scftwore olsc ships secerol certicol opplicoticns fcr the educo-
ticnol sectcr ond fcr Joco decelcpment.
451
Chapter 26
□ Clasansand methods
□ Inheritance atiel deriention
□ Encapsulatien
□ Abstlattien
□ Pelynelphisn
Classes and methods give mo! compleleobiectorleneaioon. It is through these that the languageprovides
a tlassahillalthy,ainhllitantlaefablhavielsaantatetlaantaattlibutls,aantathlaabilityateatllivlanlwaebjltts
frem existing enas. Encapsulation means that any interaction between objecSs sswell defined . Data
ewnad by enl ebjltl, fer lxempll, is bidden frem ollnis. Cemmunitetien baIwaan ebjltls etturs
enly ttreu.t mliia.li, bltauil an ebjltt’i tata ant le.it (er mltteti) arl lntapiulatlt te.lttlr.
Objltt erilntetien else preiidls abstraction, ttl ebility te dlfinl pre.remmin. prebllms ttreu.t
ttl ti.tlr-llill peredi.m ef intlrettin. ebjltts. Finelly, ree! supperts polymorphism, ttl ebility fer
eplreters te epply es eppreprietl te ttl kind ef dete end mlsse.ls inielild. Fi.url 26-2 summerizls
tew ttlsl nlw tentlpts lxpend tlessit Rlxx inte ttl rlelm ef full ebjltt erilntetien.
Classic Rexx
Encapsulation
Abstraction
Polymorphism
Figure 26-2
452
r4 and Object-Oriented roo!
If you are not familiar with object-oriented programming, we introduce it in the tutorial on object-
oriented Rexx programming in Chapter 28. That chapter uses Open Object Rexx as a basis for
explanation.
For those who are familiar with object programming and terminology, let’s describe how roo! achieves
object orientation. To start with, roo! extends classic Rexx with a set of object-oriented features. Figure
26-3 expresses how roo! extends classic Rexx into the world of object orientation.
Figure 26-3
□ New operators:
□ AA (double caret) — Instancecreation
□ a (caret) —Method invocation prefix operator
□ ~ (tilde) — Method invocaation infix operator
□ [ ] (brackets) — Arraylikereference operator
453
Chapter 26
□ Instance keyword ior the datatype built-in iunction periorms actions only on properly cre
ated object instances. This enables error-checking.
□ The trace is extended to objects.
□ New built-in iunctions:
□ callback —Transiers control to a callback routineunder certain conditions
□ Adds stack commands makebuf, dropbuf, newstack, and delstack ior compatibility with
other Rexx implementations
□ A roo.dll module allows using roo! capabilities irom C and C++, while Java Native Interface or
JINI provides Java integration.
roo! also includes several extensions based on NetRexx (which is described in chapter 30), the ANSI-1996
standard, and Java:
□ loop over instruction processes all elements oi objects or compound stem groups.
From this list, you can see that roo! adds the key elements required ior object orientation. These are the
class library, with the means to code classes and methods, the new operators ior OOP, the special methods,
and reierence objects. Most oi the other language additions extend roo! to cover the kind oi extensions
iound in other Rexx interpreters. These include, ior example, the new built-in iunctions, better exception
handling, and the like. roo! oiiers a nice combination oi object orientation plus key convenience ieatures
to round out the toolset.
454
r4 and Object-Oriented roo!
The power of roo! resides in its extensive class library. In the world of object-oriented programming,
the bigger and more powerful the class library, the easier scripting becomes. More classes and methods
directly translate into less work for the developer.
Of course, each class has its own group of built-in methods appropriate to the class. The methods are too
extensive to list, but you’ll get an idea of their range from this alphabetical class list. Skim the class list
and you’ll understand the power of classes and what roo! has to offer:
Class Use
455
Chapter 26
Class Use
Learning roo! means learning the class hierarchy and their associated methods. Leveraging this built-in
power reduces the code you write because it utilizes the object-oriented tools. This is the power of an
object-oriented Rexx interpreter. The product documentation and sample scripts provide the information
you need to build your knowledge as you go. The roo! tutorials are especially designed to help classic
Rexx programmers transition to object-oriented scripting in an easy manner.
Summary
r4 and roo! are free Rexx interpreters from Kilowatt Software. Both are Windows-based products that
conform to the TRL-2 language standards. This chapter discusses some of the unique features of these
products beyond the language standards, concentrating especially on roo! and its unique object orienta
tion. roo! is a powerful free object-oriented Rexx for Windows systems that offers an alternative to Open
Object Rexx.
The specific features we covered in this chapter included the strengths of r4 and roo! as Rexx inter
preters, how to download and install them, and where to find the product documentation. We then
described the toolset that works with both the interpreters and is also freely downloadable. After briefly
discussing the basic characteristics of object-oriented programming, we listed and describe the major
object-oriented features of roo!. The idea was to give experienced object-oriented programmers a quick
summary of roo!’s object features.
The next chapter introduces Open Object Rexx, another object-oriented Rexx interpreter. Chapter 28 pro
vides a full tutorial on object-oriented scripting. It leverages your knowledge of standard Rexx scripting
to introduce object-oriented programming through program examples.
456
r4 and Object-Oriented roo!
457
n->
___ I _J
ooRexx provides easy entry into the world of object-oriented programming, or OOP. The key
benefit is that Rexx is an easy language to learn and grow with, and ooRexx retains these
advantages while supporting object-oriented scripting.
Since standard or "classic" Rexx scripts typically run under Open Object Rexx without any
changes, ooRexx makes it easy to migrate to object-oriented programming while preserving
investment in traditional Rexx code. Existing code can run as is, while new code is added to take
advantage of object-oriented features. Developers may still code in classic, procedural Rexx while
adding object-oriented features at any rate they find desirable.
In contrast to other object-oriented languages, such as C++ or Java, Open Object Rexx emphasizes
Rexx’s traditional strengths in simple syntax and ease of use. Yet it combines them with power. If
you have experience in other object-oriented languages, you may be interested to see how ooRexx
compares.
Leveraging ooRexx means using its object-oriented features. The next chapter presents a tutorial on
object-oriented scripting using ooRexx. The tutorial assumes no background in object-oriented
programming, so if you don’t know OOP, this lesson will start you writing your own object-
oriented scripts. The goal is to leverage your knowledge of classic Rexx to launch you into object-
oriented scripting.
Chapter 27
Origins
IBM developed a product they named Object REXX in the mid-1990s. They offered this interpreter for
the Windows, Linux, Unix, and OS/2 platforms. In 2005, IBM open-sourced the product and gave it to
the non-profit Rexx Language Association. The "RexxLA" has developed, supported, and maintained
it ever since.
The official product name is Open Object Rexx, but it's nearly always referred to by its shorter moniker,
ooRexx. The RexxLA website is www.RexxLA.org, while the ooRexx website is at www.ooRexx.org.
ooRexx runs on Windows, Linux, Unix, BSD, and macOS. It is free and open source software.
Features
Many believe that object-oriented programming is superior to traditional, procedural programming. Its
benefits include:
□ Gteater qitylOzy assurance and a lower er rot rate fhrough reusiogpaoven components
Ooeo Object Rexx cecoletely exteods Rexx iote OOP. It tss sll oessible OOP festrres ioclrdior:
□ Objects, clssses, srbclssses sod sroerclssses, cets clssses, cixio clssses, orblic sod orivste cetteds
□ Ioteritsoce
□ Mrltiole ioteritsoce
□ Eocsosrlstieo
□ Pelycerotisc
□ Metted ctsioior
□ InSerfates io aincrediWle wide rangeof tooTt. 'I'heoa includethose dishrthuted weth ooRexxitsnlf, as
aell Rexx teels svsilsble elseatere. Ao exscole sre tte esseotisl develeoer teels svsilsble st
www.Rexx.org.
460
Open Object Rexx
Open Object Rexx can be used in place of shell scripts. The advantages to ooRexx scripting include:
□ PArtabclcti
□ Extua uiitiiien and ioterteren (inpiudins OO-cpeciA loslc for UPlndowc, UXtun, and Unix)
Figure 27-1 onmmdcizro oao- aS 1W- major S-atnc-e aS Open Object R-xx that go brysuP tht R-xx olau-
ppcpe pnp iapeeci rtxx cnttcoctttce. It ewsle twpt ssrtxx ce p eeotcett ss iapeeci rtxx twpt pppe cpnr ntl
Staterte.
461
Chapter 27 CCCC
Installing ooRexx
You can freely download ooRexx from its permanent project library at SourceForge at
https://sourceforge.net/projects/oorexx/files/oorexx/ .
You'll find ooRexx there as well as a number of tools and interfaces for it. Once you access the Files
panel to download the latest release of ooRexx, you'll see ready-to-install packages for Windows,
Linux, BSD, and the macOS. These include Windows self-extracting .exe files, Linux packages in
popular formats such as .deb and .rpm, and .dmg files for macOS. For BSD you'll find compressed
archive files.
The easiest way to install ooRexx is simply to pick the package that matches your operating system
and its modality (either 64 or 32 bit), download it, and double-click on it to start your installer.
First, however, be sure to read any background information contained in the INSTALL and ReadMe.txt
files. The CHANGES.txt and ReleaseNotesmay also be useful prior to installing.
Windows
By default, the product installs into a new directory:C:\Program Files\ooRexx. Also by default,
the install creates Windows file associations and uses the file extension .rex for ooRexx scripts. (The
example scripts in this book follow the alternative convention of using the file extension .orexfor
ooRexx.)
You can also set some defaults on how Windows treats an ooRexx script when you double-click on it to
run it. For example, rexxpause allows you to click on a script to run it, and then automatically pauses
so that you can see its output in a command window.
We recommend checking the box to place an ooRexx icon on your desktop. This folder gives easy
access to all ooRexx resources.
After the install, the entry Open Object Rexx will appear in your Windows Program List. Find it and
click on the ooRexx Samples entry to run a sample program to verify your install.
Finally, developers often ask if installing ooRexx on a Windows computer that already has Regina
Rexx installed causes any conflicts or problems. We can't speak to all releases or their combinations,
but the author has never experienced difficulty in co-residing the two products on various computers.
Windows keeps the two apart and distinguishes them by using different install directories, file
associations, and the like.
Linux
After downloading the package file that matches your distribution and package manager, simply
double-click to install ooRexx.
462
Open Object Rexx
The directories ooRexx uses depend on your platform and its release. For the current Ubuntu
download, ooRexx places its binary executable rexx into directory /usr/local/bin.
Example programs havefile name extension of.rexand are placed under the directory
/usr/local/share/ooRexx.
This means that no conflicts result for a computer that already has Regina installed. Just
run ooRexx via the rexx command and Regina via its regina command:
Or code the first line of your scripts to invoke the correct interpreter. For example, code
this first line to run the script under ooRexx:
#!/usr/local/bin/rexx
#!/usr/bin/rexx
These locations do vary by the release of your download, so be sure to check your filesystem after
installing ooRexx so that you know exactly where it places its executables, demo examples, and
documentation on your system.
The Basics
The next chapter presents a complete tutorial on Open Object Rexx scripting. Here we discuss some of
the basics to show how the language supports object-oriented programming. We define classes, meth
ods, messages, attributes, abstract classes, instance classes, public and private methods, directives, and
other object-oriented terms. We'll present brief code snippets to make these terms clear.
If you follow this chapter, but do not know how to use these concepts in developing object-oriented
scripts, don’t worry. That is the purpose of the next chapter, Chapter 28. That chapter provides a full,
step-by-step tutorial on how to write object-oriented scripts. Here the idea is just to introduce a few
key object-oriented concepts and to show how Open Object Rexx implements these concepts in code.
Rexx objects containmethods and variables. Variables are more accurately called attributes. Object methods
are actions that are invoked by sending a message to the object. Rexx uses the tilde operator (~) to send
the message to the object and run its method.
For example, in classic Rexx, you might “check in” a library book by coding a function called check_in.
Then invoke the function like this:
463
Chapter 27
In Open Object Rexx, send a message to the object book to run its check_in method:
Rexx runs the method that matches the message name following the tilde or twiddle. Note that the method is
encoded to the right of the object to which it applies. Thus the ordering of object~method varies from the
traditional ordering of function(parameter).
Object variables or attributes are exclusively owned by the object. Attributes are encapsulated in that they are
not visible or accessible by other objects and their methods; only the object’s methods can change its
variables. Each method in the object specifies which variables it accesses by coding them on its expose
instruction. Any changes a method makes to an exposed variables remains after the method terminates.
Objects are created by the new method. For example, to create a new book in the library system:
new_book = .book~new
The new method creates the object and automatically invokes the init method (if it is defined) to
initialize object variables from parameters and defaults. Here’s an example that adds a newly acquired
book to the library and sets some attribute values:
The parameters for a method immediately follow the name of the method and are enclosed in parentheses.
The preceding statement encodes three parameters inside of the parentheses for the method.
To later delete the book permanently from the library system, use the drop instruction. This instruction
needs only a single operand, the name of the object instance to delete:
drop new_book
A group of related objects compose a class. An individual object created from the class, such as the book
referred to earlier, is called an instance or instantiation of that class.
To identify classes and methods in Open Object Rexx code, use directives. Directives are denoted by leading
double colons (::). This example creates the book class and methods to check books in, check them out, and
initialize a new book instance. The class and each of its methods are denoted by the appropriate directive:
464
Open Object Rexx
::class book
::method check_in
. . .
::method check_out
expose title book_id patron_id
. . .
::method init
use arg title, publisher, pub_date
. . .
This example also shows how the new expose instruction makes attributes available to a
method that can change them. If the method alters any values listed in the expose instruction,
these new values persist even after the return or exit from the method. So, the check_out
method shown here can change the three arguments listed in its expose instruction. If an
expose instruction is coded, it must appear as the first instruction in the method.
The new use arg instruction shown here retrieves the argument objects passed in to the method.
It differs from the arg and parse arg instructions in that it allows nonstring arguments. The
use arg instruction performs a direct, one-to-one assignment of arguments to variables. So, the
init method in the example accesses three arguments through its use arg instruction.
Methods may be eitherpublicorprivate. Those that may be invoked from the main program or
other classes are public. Those available only to the methods within the class are private. By
default, methods are public. Use the keyword private to define a private method. In the
example that follows, the method replacement_cost can only be invoked by other methods in
the book class. The method can access and update any of the three attributes listed on its expose
instruction. As shown here, it updates the replacement_amount value:
::class book
::method checkin
::method checkout
Methods can be invoked by either of two operators: the tilde (~) or the double tilde (~~). Using ~
returns the result of the method, while using ~~ returns the object that received the message.
Most situations call for returning the result of the method, so the tilde (~) appears more
commonly in code.
Several methods may be applied to an object, one after another, by a technique referred to as
method chaining. This example applies two methods to the book object, with the check_out
method returning the result:
465
Chapter 27
Open Object Rexx supports several kinds of methods. Instance methods perform an action on a
specific object or instance of a class. They handle the data of a specific object. Class methods are more
global. They apply to the class as a whole. For example, class methods might count the number of
objects in the class or manage all the objects together as a collection.
Classes are organized into class hierarchies. These describe inheritance relationships in a hierarchical
manner. A subclass inherits all the attributes and methods of its parent class or superclass. As well, the
subclass adds its own attributes and methods as needed. For example, hardback_book and
paperback_book could be subclasses to the book class. Each inherits all the behaviors of thebook
class (defined by its attributes and methods) but adds additional behaviors as well:
::class book
::method checkin
An abstract class is one for which you would not normally create instances, but whose definition could be
useful to define common generic behavior. For example, along with the book class there might also be a
magazine class. Both could be subclasses of the abstract class library_resource. This superclass might
include some generic behavior common to any library resource, be it a book, magazine, video, or whatever.
It is an abstract class that we choose never to create an instance of, as we only use it as a superclass to define
some behaviors for its subclasses:
Some object-oriented languages only allow subclasses to inherit attributes and methods from a single
superclass or parent class. Open Object Rexx permits multiple inheritance. An object can inherit from both its
direct parent and also from other classes called mixin classes. Instances are not generated from mixin
classes. Instead mixin classes are just containers of attributes and methods that subclasses can inherit.
A subclass can inherit behaviors both from its single parent class and from one or more mixin classes.
In this example, the book class inherits variables and methods from both its abstract parent class
library_resource and the mixin class named printed_resources :
466
Open Object Rexx
Open Object Rexx supportspolymorphism, the ability to send the same message to different
kinds of objects and invoke the appropriate method in each. For example, the library might
require different actions to check out reference books as opposed to regular books:
book~check_out
reference_book~check_out
In each case, Open Object Rexx runs the different check_out method appropriate to the object to
which it is applied.
Rexx objects interact concurrently. Multiple objects may be active at any one time, sending
messages and executing methods. It is also possible to permit concurrency within an
object when that makes sense to the application. In this situation multiple methods within
an object run at the same time. An example script in the next chapter shows how to
implement concurrency.
How much work developers must perform to create their applications varies inversely to
the size and power of the class libraries: the more extensive the class libraries that come
with the product, the less work developers must do. ooRexx’s class libraries are quite
comprehensive, designed to support high developer productivity.
At last count, ooRexx had some 50-odd classes. That's too many to list here, so see Appendix I
for the complete list.
Classes arrange into a class hierarchy. This design supports one of the biggest benefits of object-oriented
programming, the inheritance of capabilities by subclasses form their parent or super class(es).
For example, ooRexx's Array class offers nearly three dozen methods that help you manipulate arrays. In
addition, it inherits several dozen methods from the three classes to which it is a subclass: the Object class,
the Collection class, and the OrderedCollection class.
467
Chapter 27
Class Use
The Fundamental Classes These classes provide the basis of the class hierarchy. They include Object,
Class, String, Method, Routine, Package, and Message classes.
The Stream Classes These classes provide for I/O. They include the InputStream, OutputStream,
InputOutputStream, and Stream classes.
The Collection Classes A collection is any group of items that can be processed in like manner. These
classes form the basis for data structures like arrays, lists, queues, sets, and
more.
The Utility Classes This largest category includes a couple dozen classes that provide time and
monitoring services, implement comparisons, support regular expressions,
manage pointers and buffers, and more.
New Operators
ooRexx retains all the operators in classic Rexx, and adds many new ones. The first two listed below allow
scripts to send a message to an object, thereby running the method associated with the message name. The
difference between the two operators is in what is returned. The tilde returns a method result, while the
double-tilde returns the object of the message. The brackets operator allows easy reference to objects within
a collection. Brackets can be used in place of the formal message descriptors to add or retrieve an object to
or from a collection. This table lists these new operators and their meanings:
Operator Use
~ Send a message to invoke a method and return its result
~~ Send a message to invoke a method, return the object of the
message
[] Add or retrieve objects to/from a collection
+=, -=, *= /=, %=, //=, ||=, &=, |=, Extended assignment operators. These combine the indicated
&&=, **= operation with the assignment
468
Open Object Rexx
Directives
Directives define a program’s classes, methods, and routines. They are processed first to
establish the classes, methods, or routines needed. They're identified by the double-colon ( :: ).
The : :class and ::method directives identify the beginning of classes and methods in your
code. They are how you define new classes and methods in your scripts. Code the directive for a
new class or method, then code the class or method right after the keyword identifier.
Directives are placed at the end of the source file containing them. There are four key directives
and several lesser-used ones:
Built-in Objects
Open Object Rexx offers a set of built-in runtime objects that are always available to scripts. These
help scripts interact with their environment, perform input and output, view environmental
parameters, and inspect return codes. This table lists these built-in objects and their functions:
469
Chapter 27
Special Variables
Open Object Rexx adds two new special variables. They are used for referencing objects because object-
oriented programming requires a convenient way to refer to certain objects. self refers to the currently
executing method, while super refers to the parent of the current object:
New Instructions
Open Object Rexx enhances many of the instructions in classic Rexx and also introduces a few new ones.
Among the new instructions, expose and guard are concerned with variable scoping and control access to
an object’s variables or attribute pool. raise is especially interesting in that it allows scripts to explicitly
raise an exception or error condition. This supports a more flexible and generalized error trap facility than
classic Rexx offers.
Instruction Use
expose Permits a method to access and update specified variables
forward Forwards the message that caused the active method to execute
guard Controls concurrent execution of a method
loop Similar to DO
raise Explicitly raises error conditions or traps
reply Sends an early reply from a method to caller, allowing concurrency
use Retrieves arguments into a program, routine, function, or method
New Functions
Open Object Rexx introduces a handful of new functions and enhances a few existing ones. Among
the new functions are the ANSI-1996 standard string functions, changestr and countstr. Other
new functions include beep, directory, lower, upper, and var.
470
Open Object Rexx
Rexx API
The application programming interface, or API, allows you to interface applications to Open Object
Rexx or extend the language.Applications are programs written in languages other than Rexx, such
as C or C++. The ooRexx manuals provide all the details on how to interface other languages to
ooRexx or extend the product’s capabilities. Full documentation downloads with the product, and
advanced sections explain the use of the API.
Class Use
WindowsProgramManager Enables interaction with the Windows Program Manager
WindowsClipboard Use the Clipboard to transfer data
WindowsRegistry Control the Windows Registry. Retrieve, modify, add, delete entries
WindowsEventLog Interact with the Windows Event Log
WindowsManager Query, manipulate, and interact with desktop windows
WindowObject Query, manipulate, and interact with a window or its children
MenuObject Query, manipulate, and interact with a window's menu or submenu
OLEObject Manages OLE events
OLEVariant With OLEObject, provides a complete interface for OLE automation
The ooRexx manual Windows Extension Reference provides complete details on all the Windows
extensions.
471
Chapter 27
Category Use
Process and thread functions Control processes and threads
User and group functions Manage user ids and groups
File and file system functions Manage files, directories, and permissions
Extended attribute functions Manage extended file attributes for those OS's that support them
Name lookup functions For retrieving information about servers and services
Miscellaneous functions A half dozen miscellaneous functions
The manual Unix Extensions Functions Reference describes all these functions in detail.
Summary
Open Object Rexx is developed, supported, and maintained as a free and open source product by the non
profit Rexx Language Association. It's a true superset of classic Rexx, as defined by the TRL-2 and ANSI-1996
standards, and so runs the vast majority of classic Rexx programs without alteration.
This chapter summarized the features of ooRexx, how they support object-oriented programming, and how
they extend beyond the classic Rexx standards. We enumerated how the language differs from classic Rexx, and
discussed the many additions and extensions to the language. We should further note that the ooRexx website
contains many useful tools for free downloading, including two different products for building GUI interfaces,
and SQLite and other interfaces for database support.
Of course, since it runs classic Rexx code, ooRexx can use any of the many utilities coded in standard Rexx.
Websites like www.RexxInfo.org and www.Rexx.org offer free downloads.
As a powerful, object-oriented language that retains Rexx's ease of use, ooRexx lies at the intersection of
three of today’s key software trends:
□ High-level scripting
□ Object-oriented programming
□ Free and open source software
472
Open Object Rexx
The next chapter presents a complete tutorial on Open Object Rexx scripting. It starts by showing
how to integrate just a few simple classes into a traditional Rexx script. Then, it proceeds more
deeply into the built-in classes and methods of the product.
Eventually, the examples lead to our developing our own classes and using them to solve
problems such as developing a stack data structure and implementing a video check-out system.
The tutorial is designed so that, even if you have no object-oriented programming experience,
you’ll be able to quickly adapt your classic Rexx knowledge to learn object-oriented scripting.
1. Is Open Object Rexx a superset of classic Rexx? Will classic Rexx scripts run under ooRexx
with- out change? What are inheritance and multiple inheritance? How are they implemented
in Open Object Rexx?
2. What are encapsulation and polymorphism, and how does Open Object Rexx support them?
3. Which class do you use to perform I/O? What advanced I/O features does this class’s
methods offer?
4. What are the new special variables of Open Object Rexx, and how are they used?
5. What are the four kinds of directives, and what are their functions?
6. What are collections and the collection class? Name five of the collection classes, and
describe their definitions.
7. How do you define and manually raise a new error condition?
8. What is the function of the expose instruction? How is it coded and used?
473
Open Object Rexx Tutorial
Overview
For those who practice object-oriented programming, the summary of Open Object Rexx provided
by the previous chapter is all you need. Install the product, look over the sample scripts that come
with it, review the class libraries and their methods, and you’re ready to program. If you are famil
iar with object-oriented programming from languages like Java, C++, or Python, review the class
libraries and start programming.
For everyone else, something’s missing. The previous chapter discussed the basics of OO program
ming with Open Object Rexx, but more to describe its capabilities than to teach you how to use it.
This chapter tries to fill that gap. For those new to object-oriented programming, it presents a simple
tutorial. Assuming that you know classic Rexx, it bootstraps you into the world of object-oriented
programming with simple, complete program examples.
Open Object Rexx is ideal for learning OOP because it retains full compatibility with classic Rexx;
everything you’ve learned about classic Rexx still applies. You can tip-toe into the object-oriented
world at your own pace by learning ooRexx. For example, you can still manipulate strings with
the string functions of classic Rexx. Or, you can start using Open Object Rexx’s string class meth
ods in their place. You can still perform traditional Rexx I/O. Or, you can use the object-oriented
stream class methods. Code with either approach in Open Object Rexx.
Open Object Rexx retains the advantages of simplicity and clear syntax from classic Rexx. It adds
the classes, methods and all the other features of object-oriented programming with a minimum
of new syntax. Concentrate on solving the problem at hand, and leverage Open Object Rexx’s new
features in the quest.
Let’s quickly discuss the sample scripts in this chapter. The first is very short; it merely tests for the
existence of a file. It illustrates how to use a stream object and a few of its methods perform I/O.
The next two scripts show you how to create your own classes and methods. Then, a more ambitious
script implements a video checkout application for a DVD library. The final example demonstrates
concurrency, where methods within a class run at the same time.
Chapter 28
A First Program
Open Object Rexx features a class library, a built-in set of classes and methods that adds a wide range of
capability to traditional Rexx. The methods in this library perform actions, much like the built-in func
tions of classic Rexx. One easy way to start object-oriented programming with Object Rexx is just to
program as in classic Rexx, but to replace function calls with object methods.
Here is an example. This simple script reads one input parameter from the command line, a filename.
The program determines whether the file exists and displays a message with its result:
The program is called File Exist. In thefirst preceding run, the inputfile exists. In the second run, the
file did not exist in the current directory. All the script does is write a message indicating whether the
file specified on its command line exists. Recall that the syntax ./oorexx is merely a way of ensuring,
on Unix-derived operating systems, that the module named oorexx in the current directory will be
invoked to run the script.
/**********************************************************************/
/* FILE EXIST */
/* */
/* Tells if a specified file exists. */
/**********************************************************************/
parse arg file . /* get user input file */
exit 0
To work with file I/O in Open Object Rexx, you can either use classic Rexx instructions such as say,
pull, parse pull, charin, charout, chars, linein, lineout, and lines, or you can use the
new object-oriented methods. This script uses the methods.
476
Open Object Rexx Tutorial
The first action when using OO input/output is always to create a stream instance:
This object manages I/O for one input or output file. To create it, we send the message new to the built-in
.stream class. The .stream class is denoted by the period immediately prior to the keyword stream.
We passed the filename from the user to the new method as its input parameter. Now we have an object
created representing an I/O stream for that file.
The following code invokes the query method on the new stream object. The method returns the null
string if the file does not exist in the current directory. If the file does exist, it returns the fully qualified
name of the file:
The lineout method is invoked with the character string to write as its input parameter. .output is one
of the special built-in objectsdescribed in Chapter 27. It is a monitor objectthat forwards the messages it
receives to .stdout, the stream object representing standard output.
Of course, if the file does exist, the script writes the appropriate message:
else
.output~lineout(‘File exists’ ) /* found the filename */
This script uses the built-in object-oriented classes and methods for I/O. This same program could be
coded using classic Rexx instructions such as say and pull, and it would have run under Open Object
Rexx as well. All classic Rexx functions have object-oriented counterparts (methods) in Open Object Rexx.
The trick to learning ooRexx is to learn its class library. Remembering what it offers as built-in classes and
methods is as important as knowing what functions are available in classic Rexx. This knowledge is the
lever that enables you to exploit the language fully and let its built-in capabilities do the work for you.
This simple script squares a number. The user enters the number as a command-line argument, and the
script writes back its squared value:
477
Chapter 28
Here is the code for this script:
/*******************************************************************/
/* SQUARE */
/* */
/* Returns the square of a number */
/*******************************************************************/
parse arg input . /* get the number to square from user */
exit 0
In this script, the first task is to create an instance to square the value. Depending on what school of OOP
you follow, this instance might also be called an object instance, the instantiation of an object class, or just an
object. We’re not concerned with formal terminology here; you don’t need to be to use ooRexx. This state
ment creates the object:
The next line in the script invokes the square method to perform the work of squaring the number. It
does this by sending the appropriate message to the object:
Look down further in the code to see the code of the square method and its class, called squared. All
classes and methods must follow the procedural code located at the top of the program. New classes
and methods are always placed at the bottom of the script. Two colons indicate a directive, an identifier
for a class, method, routine or external code block. This line defines our new class called squared:
After the class directive, we place our methods and their code. This class has but a single method named
square. This line defines this method:
478
Open Object Rexx Tutoria l
The name of a method is a character string. The interpreter matches this string with the message sent
to the object (to invoke it) in the procedural code. Here the method name is coded within quotation
marks, as many programmers prefer. You can also define a method name without the quotation
marks:
The method’s code immediately follows this method directive. Its first line reads its input argument:
In Open Object Rexx, you code use arg instead of arg or parse arg to retrieve one or more input
arguments. The method squares the number provided in the input argument and returns it as its return
string through the return instruction:
That’s it! You can create your own classes and methods in this fashion. Run the methods in those objects
in the same way that you run the built-in methods provided by ooRexx.
In many ways, creating objects and methods is similar to creating subroutines and functions in classic
Rexx. The difference is in the hierarchical structure of object-oriented programming. The ability to
inherit behaviors (attributes and methods) through the class hierarchy minimizes the amount of new
coding you have to do. This advantage becomes most pronounced in large programming projects,
where the chances for code reuse are higher. It becomes significant in any situation in which you can
optimally leverage ooRexx’s built-in class library.
The user enters a shell name, csh, and the script responds with its full name. Here’s another interaction:
The user enters the shell for his or her operating system, COMMAND, and the program responds that the
shell is used as a Windows command window prompt. The program recognizes a couple other inputs
(ksh and CMD), but comes back with this message for any other input:
479
Chapter 28
/*****************************************************************/
/* WHICH OS */
/* */
/* Tells which OS system you use depending on the command shell. */
/*****************************************************************/
os = .operating systems~new /* create a new object */
This script only contains three lines of procedural code. The first line creates a new instance of the class
.operating_systems. Send the class the new method message to create a new instance of the class:
The second line in the program runs the method write_command_shell in the class. This method does
all the real work of the program:
The last line of procedural code, the exit instruction, ends the program. Class and method directives
follow, along with the code that defines them. The class(es) and their method(s) are always placed at the
bottom of the code in the script. This line defines the .operating_systems class:
This class is followed by its two methods. The first is the init method:
480
Open Object Rexx Tutorial
The init method is a specially named method. Open Object Rexx always runs the init method (if
there is one) whenever its creates a new instance via the new message. So, the first line in the script not
only created a new instance of the operating_systems class but also automatically ran the init
method.
In the init method, the first line of code uses the expose instruction to access the variable named
shell. By using expose, the method has read and update capability on any variables or attributes it
names. The expose instruction is the basic technique by which attributes can be shared among methods
in a class:
After accessing this variable, the init method prompts the user to enter a shell name, reads that user
input, and returns:
say 'Enter the shell name:' /* prompt and read user input */
parse pull shell .
return
The second line of code in the driver runs the write_command_shell method. This method also
accesses the attribute shell by its expose instruction:
expose shell
Then, the method executes a select instruction to determine the full shell name or associated operating
system, and its writes this response to the user:
When this method returns, the main routine or procedural code terminates with an exit instruction.
This script shows how user interaction can be encapsulated within classes and methods. The procedural
code in the main routine or driver can be minimal. The classes and their methods perform all the work.
Once you start thinking in object-oriented terms, you’ll develop the knack of viewing problems and their
solutions as groups of interacting objects with their logical methods.
481
Chapter 28
□ Director)a — A e<<llentinn indexed byurnquechaeacSersgrings
□ List — A uenuenced eoiiention whichollowsinseets ndan\'r|i<o><ti<>n
□ Queue — A uenuenced e<ollentionthae ollowsinseetsne Shestectcar end i no poeitions
□ Reliilen —t <elle<ilen olin nenunluue ebje<is fec lnnenes
□ Sei—t unluue uelleuilen ef ebjeuis (subuless ef Teble)
□ Teble —t uelleuilen olin unluue ebjeuis fec lnnenes
Yeu’ll ceceld neve ie uceeie deuc eon neie sicuuiuces olin ell inls bulli-ln ieoec evelleble. Bui fec ine
iuciese ef lllusiceilen, oe use ine List uelleuilen uless es ine besls ie lmilemeni e sieuk ln ine neni
semile suclii.
Tne suclii icemiis ine usec ie eniec sevecel llnes enn inen ine kedoecn exit. Hece ine usec eniecen feuc
llnes ilus ine kedoecn exit. Tnen ine suclii ieis ine sieuk ie ceicleve enn iclni ine liems. Slnue e sieuk ls
e lesi-ln, flcsi-eui (LIFO) neie sicuuiuce, ine liems nlsiled ln ine cevecse ecnec ln onlun ined ece eniecen.
Heo ne oe neslgn inls suclii? Flcsi, lnenilfd ine sieuk es ine enilid ec objectolin onlun ine suclii oecks.
Tnls sneuln be e uless inei oe uen lnsienileie bd uceeilng en ebjeui.
Seuenn, icd ie lnenilfd onlun eieceilens ec methodsneen ie be eneuuien en inei ebjeui. Pusn enn iei
ece ioe ked sieuk eieceilens, se oe’ll neen e meinen fec eeun ef inese. Fucinec cefleuilen leens ie ine
ceellzeilen oe else ceuulce en lnlilellzeilen meinen (init) enn e meinen ie ceiucn ine numbec ef liems
en ine sieuk.
Inenilfdlng ebjeuis enn inelc meinens ece ine beslu sieis ln ebjeui-eclenien neslgn. One einec siei (nei
celeveni ie inls slmile enemile) ls neiecmlnlng ine celeilensnlis ec lnieceuilens beioeen ebjeuis.
Se, ine suclii olll neve ene uless, uellen stack, enn feuc meinens ln inei uless. Hece ece ine meinens enn
inelc funuilens:
□ push —Pleues en liem (ec llne) enie ine sieuk (pushesine liem)
482
Open Object Rexx Tutorial
With this understanding of what objects and methods are required, we can write the program:
/**********************************************************************/
/* STACK */
/* */
/* Implments a Stack data structure as based on the LIST Collection */
/**********************************************************************/
the_stack = .stack~new /* create a new stack object */
exit 0
As in previous scripts, the first action is to create an instance of the class object. Here we create a stack to
work with:
483
Chapter 28
The script reads a line with the linein method applied to the default input stream via the .input mon
itor object. This input line is then acted upon by the translate method. The code chains these two
methods so that they execute one after the other. This reads and translates the input to uppercase.
For each line the script reads, it places it into the stack. This runs the push method with the stack_item
as input:
After all user-input lines have been read and placed onto the stack, this loop retrieves each line from the
stack via the pop method and writes them to the user:
The loop invokes our method number_items on the stack to determine how many items are in the stack.
All the methods expose the stack so that they can work with it and read and alter its contents. Here is
the code that exposes the program’s shared attribute:
Let’s discuss each of the methods in this program. The init method automatically runs when the stack
object is first created; it merely creates a new List object. Recall that we selected the built-in collection class
of type List with which to implement our stack. This line in the init method creates the new List object:
The push method grabs its input argument and places it into the stack. It uses the List class’s insert
method to do this. The first argument to insert is the line to place into the list, while the second is the
keyword .nil which says to place the item first in the list:
The pop method checks to see if there are items in the stack by executing the List class’s method items.
If the List contains one or more items, it returns the proper item from the List by the remove method:
If there are no items in the List, the pop method returns the NIL object. Coded as .nil, this represents the
absence of an object (much in the same manner the null string represents the string with no characters).
The method number_items simply returns the number of items currently on the stack. It does this by
running the List method items:
484
Open Object Rexx Tutorial
To summarize, this script shows how you can use an object and Open Object Rexx’s built-in collection
classes and methods to create new data structures. With its built-in collection classes, ooRexx is a rich
language in terms of its support for data structures.
After each action is complete, the script clears the screen again and redisplays the menu. The program
terminates when the user selects the option: X. Exit.
The program error-checks for logical mistakes. For example, it will not let the user check out a video that
is already checked out, nor will it allow the user to check in a movie that is already on hand. The script
always ensures the title the user refers to is in the collection. If not, it writes the appropriate message to
the user.
As in the stack program, the Videos program implements an in-memory data structure to control the
videos. This script uses the Directory built-in collection class to make an indexed list of videos. The film
title is the index into the Directory; the sole data item associated with the video’s title is its status. The
status can either be in library or checked out. For simplicity, the program assumes that there is only
a single copy or DVD for each movie title.
The Directory of videos will be the class called movie_dir. The methods for this class are:
485
Chapter 28
Here is the script:
/*****************************************************************/
/* VIDEOS */
/* */
/* An in-memory circulation system for films on DVD. */
/*****************************************************************/
movie_list = .movie_dir~new /* create new Directory object */
‘clear’
say “1. Add New DVD” ; say “2. Check Movie Out”
say “3. Check Movie In” ; say “4. Show Movie Status”
say “5. Remove Lost DVD” ; say “X. Exit”
select
when selection = ‘1’ then movie_list~add_movie(title)
when selection = ‘2’ then movie_list~check_out_movie(title)
when selection = ‘3’ then movie_list~check_in_movie(title)
when selection = ‘4’ then movie_list~check_status(title)
when selection = ‘5’ then movie_list~lost_or_destroyed(title)
when selection = ‘X’ then exit 0
otherwise say ‘Invalid selection, press <ENTER> to continue...’
end
pull . /* user presses ENTER to continue */
end
exit 0
486
Open Object Rexx Tutorial
end
else /* if title is not new, err message*/
say ‘Movie is already in collection:’ title
The first line of procedural code creates the instance or object the script will work with. This is the same
approach we’ve seen in previous example scripts, such as the Stack and Which OS programs:
487
Chapter 28
The next block of code clears the screen, displays the menu and reads the user’s selection. The select
instruction runs the proper method to perform the user’s selection. The .movie_dir class and its six
methods follow the procedural code. Let’s briefly discuss each of the methods.
The add_movie method checks to see if the title the user entered is in the circulating collection. It uses
the at method on the Directory object to do this:
If .nil is returned, the script adds the title to the circulating collection (the Directory) with the status in
library:
The put method places the information into the Directory. To check out a DVD, the method
check_out_movie uses the setentry method on the Directory to alter the DVD’s status:
Method check_in_movie similarly runs the setentry built-in method to alter the DVD’s status:
Method check_status executes method at to see whether or not a film is checked out:
It then displays an appropriate message on the status of the video by these lines:
Finally, the method lost_or_destroyed removes a title from the circulating collection by running the
Directory remove method:
Like most of the collection classes, Directory supports alternative ways of invoking several of its meth
ods. Here are a couple alternative notations:
488
Open Object Rexx Tutorial
For example, we could have written this line to add the new title to the Directory:
This statement is the direct equivalent of what we actually coded in the sample script:
Similarly, we could have checked for the existence of a title by referring to mv_dir[title] instead of
coding the at method as we did. This is another way of coding that reference:
This script employs simple say and pull instructions to create a simple line-oriented user interface. Most
programs that interact with users employ graphical user interfaces or GUIs. How does one build a GUI
with Open Object Rexx? This is where the power of built-in classes and methods comes into play. The
menu becomes a menu objectand the built-in classes and methods activate it. Creating a GUI becomes a
matter of working with prebuilt objects typically referred to as widgetsor controls. Chapter 16 discusses
graphical user interfaces for object-oriented Rexx scripting. Besides a GUI, the other feature that is miss
ing from the Videos script is persistence, the ability to store and update object data on disk. This simple
script implements the entire application in memory. Once the user exits the menu, all data entered is lost.
Not very practical for the video store that wants to stay in business!
Object-oriented programmers typically add persistence or permanence to their objects through interfac
ing with one of the popular database management systems. Among commercial systems, Oracle, DB2
UDB, and SQL Server are popular. Among open-source products, MySQL, PostgreSQL, and Berkeley DB
are most popular.
GUI and database capability are beyond the scope of our simple example. Here the goal was to intro
duce you to object-oriented programming, not to get into the technologies of GUIs and databases. Of
course, these interfaces are used by most real-world object-oriented Rexx programs. Using class libraries
and methods reduces the work you, as a developer, must do when programming these interfaces.
Concurrency
Object-oriented programming with Open Object Rexx is concurrentin that multiple objects’ methods may
be running at any one time. Even multiple methods within the same object may execute concurrently.
The following sample script illustrates concurrency. Two instances of the same class are created and exe
cute their single shared method concurrently. To make this clearer, here is the script’s output. The inter
mixed output shows the concurrent (simultaneous) execution of the two instances:
489
Chapter 28
Object #1 and Object #2 are two different instances of the same class. That class has one method, called
repeat, which displays all the messages seen above, from both objects (other than the one that states
that the Driver is now terminating.)
/************************************************************************/
/* CONCURRENCY */
/* */
/* Illustrates concurrency within an object by using REPLY instruction */
/************************************************************************/
object1 = .concurrent~new /* create two instances, */
object2 = .concurrent~new /* both of the CONCURRENT class */
The script first creates two separate instances of the same class:
The next two lines send the same message to each instance. They execute the method repeat with two
parameters. The first parameter tells repeat for which object is it executing (either 1 for the first object
or 2 for the second). The second parameter gives the repeat method a loop control variable that tells it
how many times to write a message to the display to trace its execution. This example executes the
method for each instance five times:
490
Open Object Rexx Tutorial
say object1~repeat(1,5) /* get 1st object running */
say object2~repeat(2,5) /* get 2nd object running */
Following these two statements, the driver writes a termination message and exits. The driver has no
further role in the program. Almost all of the program output is generated by the repeat method, writ
ten to show its concurrent execution for the two instances.
Inside the repeat method, this line collects its two input arguments. It uses the use arg instruction to
read the two input arguments:
The next line issues the reply instruction. reply immediately returns control to the caller at the point
from which the message was sent. Meanwhile, the method containing the reply instruction keeps
running:
In this case, reply sends back a message that tells which object is executing and how many times the
repeat method will perform its do loop. Now, the repeat method message loop continues running.
This code writes the message five times to demonstrate the continuing concurrent execution of the
repeat method:
do reps
say ‘Object #’|| who_am_i ‘is running’ /* Show object is running */
end
This simple script shows that objects and methods execute concurrently, and that even methods within
the same object may run concurrently. Open Object Rexx provides a simple approach to concurrency that
requires more complex coding in other object-oriented languages.
Summary
This chapter introduces classic Rexx programmers to Open Object Rexx. It does not summarize or
demonstrate all the OO features of ooRexx. Instead, it presents a simple tutorial on the product for those
from a procedural-programming background.
We started with a very simple script. That first script created a stream instance and demonstrated how to
perform object-oriented I/O. Two more advanced scripts followed. These showed how to define and
code your own classes and methods. The Stack sample program was more sophisticated. It defined sev
eral different methods that demonstrated how to employ the List collection class to implement an in
memory stack data structure. The Videos application built upon the same concepts, this time using the
Directory collection class to simulate a video circulation control system. Finally, the Concurrency script
illustrated the use of two instances of the same class and the concurrent execution of their methods.
Open Object Rexx is ideal for learning object-oriented programming. It retains all the simplicity and
strengths of classic Rexx while surrounding it with a plethora of OO features and the power of an exten
sive class library.
491
Chapter 28
5. Do the stream class methods offer a superset of classic Rexx I/O functions? What additional
features do they offer?
6. What are the four kinds of directives and what is each used for? Where must classes and meth
ods be placed in a script?
7. What symbols commonly express at and put in many collections?
8. What are the error (.error), input (.input), and output (.output) monitor objects used for?
492
""
____ I
Mainframe Rexx is important for several reasons. Rexx was originally offered on the mainframe
(specifically for VM/CMS). VM influenced early development of the language, leaving its imprint
in various ways. For example, the stack is a VM/CMS feature, and many VM/CMS commands
send their output to the stack. The stack remains a popular means for command I/O in
mainframe Rexx today, and most of the free Rexx interpreters support the stack in this role.
Mainframe Rexx is important because of the key role mainframes continue to play in many IT
organizations. While the mainframe has a low profile in the trade press and in industry buzz,
thousands of sites worldwide continue to rely on mainframes for their most vital business
operations. Mainframes remain critical to organizations around the world. Rexx is the
predominant mainframe scripting language. No other scripting language comes anywhere close
to Rexx’s usage level on the mainframe. Nor does any other scripting language interface to so
many mainframe subsystems and address spaces.
Many readers will face the prospect of porting Rexx code from the mainframe to other platforms.
This often means changing IBM mainframe scripts into free Rexx scripts that run under Linux,
Unix, or Windows. Briefly comparing the differences between mainframe Rexx and open source
Rexxes may be useful.
IT professionals will want to apply their skills across many platforms. They may have learned
Rexx on the mainframe and now wish to script on some other platform. Or, they may be from
the Windows, Linux or Unix background and are unfamiliar with mainframes. Rexx offers a
quick means to become instantly productive across varied platforms. You can easily transfer
your Rexx skills from Windows, Linux, or Unix to the mainframe environment or vice versa.
Chapter 29
This chapter describes the differences among IBM mainframe Rexx, the Rexx standards, and the free
Rexx interpreters this book discusses. The goal is to sketch the major differences between IBM
mainframe Rexx and the free Rexxes, as well as outline the extended features of mainframe Rexx.
VM Rexx Differences
Rexx differs in slight ways across the three mainframe operating system families, which we refer to
generically as VM, OS, and VSE. All IBM mainframe Rexx implementations meet the TRL-2 standard
(with one or two very minor exceptions, which we’ll discuss).
Almost all of the special features of mainframe Rexx are language extensions. This section summarizes
them for VM Rexx. The information is based on the VM REXX Reference manual. Where they fit in, we
add a few comments on OS/TSO Rexx, based on TSO/E REXX Reference and TSO/E REXX User’s Guide.
The next section, “OS/TSO Rexx Differences,” explores the extended features of OS/TSO-E Rexx in
greater detail. You can freely download all Rexx-related IBM manuals from www.RexxInfo.org.
This section compares VM Rexx to the ANSI-1996 standard and also enumerates many of its
differences from free Rexx interpreters for other operating systems such as Linux, Windows, and Unix.
Figure 29-1 summarizes many of the major differences:
First line
The first line of a VM/CMS Rexx script must be a comment. The first line of an OS TSO/E Rexx script
normally must contain the word REXX. We recommend the first line of any mainframe script contain this
first line, which satisfies both requirements:
/* REXX */
This first line is portable across almost all Rexx interpreters on all systems. The primary exceptions are
those Linux, Unix, or BSD environments in which the first line of code traditionally indicates the
location of the Rexx interpreter (such as: #!/usr/local/bin/regina ). Running scripts explicitly on
these systems avoids the need for a first line that indicates where the Rexx interpreter resides, in most
cases.
494
IBM Mainframe Rexx
Extras and
Environmental
Additions
Figure 29-1
To get information about a Rexx error message, enter help with that message id. Here is the generic
command template:
help msgid
For example, for help on message ID dmsrex460e, enter that message ID as the operand on the help
command:
help dmsrex460e
495
Chapter 29
File types
Mainframes use different file-naming conventions than other platforms. These differences apply to files
containing Rexx scripts in several ways. Under VM, Rexx scripts typically reside in files of type exec.
For this reason, Rexx scripts are often referred to as EXECsor REXX EXECs.
VM Rexx scripts designed to run as editor macro commands(or editor scripts) have the file type of xedit.
These are referred as edit macros. Edit macros issue commands to the XEDIT Editor and can automati
cally drive the editor. Or, they can extend the editor’s functionality or tailor or automate its use.
CMS pipelines use the file type rexx to identify a stage or program in the pipeline. CMS pipes are an
alternative to the stack and provide a general-purpose communications vehicle popular on VM main
frames. Each stage in the pipeline manipulates or processes data.
In OS (or TSO) environments, Rexx scripts may either be sequential data sets or members of partitioned
data sets (PDSs). PDS libraries of scripts are most common. Filenames for Rexx scripts under TSO/E
normally end in exec.
“Not” symbol
Mainframe scripts often use this symbol - as the “not” operator, for example, as in “not equal” -=. We
recommend using the universally accepted, ANSI-standard backslash instead. For “not equal,” for exam
ple, use: \=. The backslash appears on all keyboards and is more transportable than the traditional main
frame “not” symbol. Other portable alternatives available on all keyboards for “not equal” include <>
and ><. This principle also applies to the other operators, such as “not greater than” ( \> ) and “not less
than” ( \< ).
OS commands
VM Rexx scripts can issue both CMS and CP commands. CMS has its own command and function
search order. Scripts can also issue subcommands. As always, limiting the number of OS-specific com
mands leads to more portable code, but there are techniques to limit the impact of OS-specific com
mands even when they must be included. For example, isolate OS-specific code in particular routines, or
code if statements that execute the commands appropriate to the OS under which the script runs.
Chapter 13 explores these techniques for code portability.
Instructions
VM Rexx adds a few new keywords for the options and parse instructions, as well as the upper
instruction. These extended features are of relatively minor importance, but we mention them here for
completeness. Note that mainframe Rexx thoroughly supports the Double-Byte Character Set (DBCS).
The DBCS supports spoken languages that require more bits for proper representation of their symbol
ogy. For example, DBCS can be used to support languages based on ideographic characters, such as
Asian languages such as Chinese, Japanese, or Korean. Here is a brief list of the mainframe-extended
Rexx instructions:
496
IBM Mainframe Rexx
Instruction Use
OPTIONS Several options specify how strings and the Double-Byte Character Set
(DBCS) are handled.
PARSE Two new keywords are added to standard Rexx:
The external keyword parses the next string from terminal input.
The numeric keyword retrieves the current numeric settings.
UPPER This translates a string to uppercase.
Functions
VM Rexx provides a grab-bag of extended built-in functions. The find, index, and justify functions
have largely been superceded by standard Rexx functions. Use wordpos, pos, right, and left instead
of the mainframe functions for more portable, standardized scripts. The externals and linesize func
tions manage I/O to the terminal (or the desktop computer emulating a terminal). Here are the extended
VM functions:
Function Use
VM Rexx also adds over a dozen functions to handle the DBCS. All begin with the letters db. We have
not listed the DBCS functions in the preceding chart.
Some standard Rexx functions are enhanced with mainframe-unique parameters. For example, the
stream function includes specifications for lrecl and recfm. These specifications describe the way files
are structured in the mainframe environment.
Appendix E provides function formats and a usage description for the additional built-in mainframe
functions.
497
Chapter 29
□ hi—Halt Interpretation
□ ts — Trace Start
□ te — Trace End
□ hx—Halt Execution
□ ht—Halt Typing
□ rt — Resume Typing
Compiler
IBM provides Rexx compilers for both VM and OS. To be more precise, IBM provides one compiler that
generates code suitable for the operating system platform on which it runs. This compiler allows you to
quickly develop scripts with the interactive interpreter, then convert finished programs to object code by
compiling them. This approach yields the benefits of an interpreter in quick development and interactive
debugging along with the compiler advantage of faster program execution for finished programs.
498
IBM Mainframe Rexx
We note that the relative performance gain one experiences from compiling varies according to the envi
ronment and the nature of the script itself. A good example of a script that might benefit from compilation
is a computationally intensive program. As another example, systems that do not execute much code out
side of the Rexx environment might also see significant benefits. Yet there are situations where the compiler
underperforms the Rexx interpreter. So, while compiled scripts are generally faster, we don’t wish to over
simplify the relative performance of compiled versus interpreted scripts in the mainframe environment.
As in other environments, the mainframe compiler can be used as a mechanism to hide source code.
Some organizations use the compiler, for example, where audit or security rules forbid the presence of
source code in the production environment. The compiler can also aid in better syntax checking. For
example, it checks all logic branches in a program, whereas the Rexx interpreter only verifies branches
that execute.
499
Chapter 29
Figure 29-2 summarizes some of the major differences between Rexx under OS/TSO versus other plat
forms. The additional features and facilities of Rexx for TSO/E outweigh the few language elements that
it lacks.
Extras and
Missing or
Environmental
Different
Additions
Additional functions
Missing conditions-
LOSTDIGITS, NOTREADY
DBCS Compiler
Figure 29-2
Let’s discuss the details of the differences between programming Rexx in OS environments versus other
platforms.
500
IBM Mainframe Rexx
GETMSG Returns a message issued during the console session. Since this
affects the system console, most sites only authorize using this
function in special situations.
LISTDSI Returns information about a data set, including its allocation,
protection, and directory.
MSG Tells whether TSO/E messages are being displayed while the
script runs. This status will be either on or off.
MVSVAR Returns information about the current session or security labels.
OUTTRAP Either returns the name of the variable in which trapped output
is stored, or returns off if trapping is turned off.
PROMPT Returns the prompt setting for the script, either on or off.
SETLANG Returns a three-character code that tells the spoken language in
which Rexx messages are displayed.
STORAGE Reads a given number of bytes of storage from a specified
address.
SYSCPUS Returns information about active CPUs in a stem variable.
SYSDSN Returns information about whether a given dsname is available
for use.
SYSVAR Returns information about the current session, including soft
ware levels, the logon procedure, and user id.
501
Chapter 29
Many of these commands duplicate functionality of the VM command environment, such as the stack
services, the immediate commands, and the execio command for input/output.
Here is a complete list of the commands along with descriptions of how they are used:
DELSTACK Deletes the most recently created stack and all its elements
DROPBUF Drops the most recently created stack buffer
EXECIO Controls and performs data set I/O; employs either the
stack or stem variables for the I/O.
EXECUTIL Changes script execution characteristics
HE Halt execution; immediately halts the script
HI Halt interpretation; immediately halts script interpretation
HT Halt typing; immediately suppresses a script’s terminal
output
MAKEBUF Creates a new buffer on the stack
NEWSTACK Creates a new data stack (hiding the current data stack)
QBUF Returns the number of buffers on the stack
QELEM Returns the number of data stack elements in the most
recently created stack buffer
QSTACK Returns the number of data stacks for a script
RT Resume typing; continues or resumes a script’s terminal
output
SUBCOM Tells whether a specified host environment exists
TE Trace end; immediately ends tracing
TS Trace start; turns on tracing
502
IBM Mainframe Rexx
These TSO/E commands are not Rexx functions, nor are they regular operating system commands. They
are special operating system commands that can only be run from with Rexx scripts.
Code them in the same manner that scripts issue commands to the operating system. For example, this
series of commands manipulates buffers and shows how Rexx scripts issue commands:
Here is a similar example that issues some of the stack commands. Note that stacks differ from buffers in
that one already exists prior to issuing the first newstack command:
The services available to Rexx scripts depend on the address space in which they run. Available services
further depend on whether the scripts are run in the foreground(interactively) or in the background(as a
noninteractive batch job).
These topics become very OS-specific and extend beyond the scope of this book. Please see the main
frame Rexx reference manuals for information on programming services and interfaces. Appendix A tells
how to freely download them.
503
Chapter 29
In contrast, mainframe Rexx doesn't quite conform to the newer ANSI-1996 standard. Like many of the
free and open-source Rexx interpreters available on other platforms, mainframe Rexx falls short of
including all the ANSI-1996 features.
First, let’s discuss how mainframe Rexx conforms to the TRL-2 standard. Scanning the VM Rexx
manuals, we were unable to find any TRL-2 standard feature or function that VM/CMS Rexx lacks.
In contrast, TSO/E Rexx differs in several small ways from TRL-2. One minor difference is that TSO/E
Rexx lacks the NOTREADY condition. Another key difference is that TSO/E Rexx does not include the
standard Rexx I/O functions in the core language. Recall that these standard I/O functions are CHARS,
CHARIN, CHAROUT, LINES, LINEIN, LINEOUT, and STREAM. These functions are all available to OS
programmers through the external function package commonly called the Stream I/O Function Package.
IBM documents it in their manual, IBM Compiler and Library for REXX on IBM Z.
VM and TSO/E Rexx developers thus have a choice between the “traditional” mainframe I/O model
supported by the EXECIO command, and the standard Rexx I/O model. EXECIO is widely used and
understood and fits well with mainframe file systems and allocation methods. Standard Rexx I/O is
simpler, portable, and conforms to the Rexx standard. Developers can use either model, or both, as they
choose. The two may be intermixed within scripts.
While VM and OS Rexx conform to the TRL-2 standard, they lack the features introduced by the ANSI-
1996 standard. Let’s briefly list of these missing elements:
In summary, both VM/CMS and TSO/E Rexx meet the TRL-2 standard, but omit most of the ANSI- 1996
standard’s additions. This fits right in with the majority of free and open-source Rexx interpreters, most
of which follow the same pattern at the time of writing.
Interfaces
Since IBM declared Rexx its official command procedures language (or scripting language), the company
has interfaced or integrated practically every one of its other products into Rexx. Many different
subsystems can be programmed with Rexx. Rexx has become the general-purpose default programming
language for interfacing to and controlling mainframe services.
504
IBM Mainframe Rexx
EXEC interface
CMS assembler macros and functions
CP assembler macros (such as IUCS and APPC/VM)
Group Control System or GCS interface and GCS assembler macros
Callable service library routines (CSL routines)
OS and VSE simulation interfaces
CP DIAGNOSE instructions
Interface into some VM control blocks
CP system services
Data record formats intended for processing by applications (e.g.: accounting records)
Calls to Rexx scripts from programs written in languages like Assembler, C, COBOL, FORTRAN,
Pascal, and PL/I
Rexx Exits—these allow applications to tailor the Rexx environment
XEDIT editor—you can write editor macros in Rexx
Interactive System Productivity Facility Editor (the ISPF Editor)
Interactive System Productivity Facility Dialog Manager (ISPF Dialog Manager)
address cpicomm—calls program-to-program communications routines that participate in the
Common Programming Interface (CPI)
CPI Resource Recovery Routines
Netview—customization using Rexx
address openvm—invokes OPENVM routines
505
Chapter 29
Describing these interfaces and showing how to use them is beyond the scope of this book (indeed, of
any single book!). The point is that you can interface Rexx to almost any tool, language or facility under
VM, OS or VSE. Rexx is the lingua franca of the modern mainframe. See the IBM documentation in
Appendix Aon resources for more information.
Rexx’s dominance is such on mainframes that many third-party vendors also use the language in their
products. For example, the setup and installation scripts for products may be coded in Rexx. Or, the
products may support a Rexx scripting interface for customers. Candle Corporation’s performance mon
itoring and automated operations products exemplify this trend. From the vendor’s standpoint, of
course, Rexx makes a nice fit. It is ubiquitous on mainframes and every customer will have it installed.
Customers can be assumed to have Rexx expertise. The language is a powerful enough general-purpose
language that it can accomplish any task. Yet it is an easy language to decipher if a customer has to
understand the vendor’s scripts.
Sample Scripts
Let’s look at a few simple scripts to illustrate aspects of mainframe scripting. The scripts are from the
VM environment and were developed and tested under various releases of that operating system. The
purpose of these scripts is to demonstrate a few of the basic techniques of mainframe Rexx program
ming. To this end, we demonstrate how to issue VM’s CMS and CP commands, how to retrieve operat
ing system command output from the stack, how to send input to OS commands through the stack, and
how to use the execio command for file I/O. These sample scripts highlight a few of the typical tech
niques one would use in mainframe Rexx scripting. We’ve kept these scripts bare bones to hone in on the
mainframe-specific aspects. So, we purposely exclude error checking, analyzing return codes, and the
other essentials of robust, real-world scripts.
Let’s take a look at the first sample script. Under VM, scripts can issue CMS commands or CP com
mands (among others). This script demonstrates how. In VM environments, CMS commands are those
issued as if they came from the interactive command line, while CP commands control various aspects
of the environment or one’s virtual machine. By default, Rexx scripts send non-Rexx commands to CMS.
Prefacing a command with the letters cp sends it to CP.
This sample script illustrates how scripts issue both CMS and CP commands. It links to another user’s
A-disk. To run the script, the user enters the userid of the other user, a virtual address for the device, and
a disk letter. Here’s the script:
if diskletter = “” then
say ‘Wrong number of arguments, error!’
else do
cp link to userid 191 as vaddr r
access vaddr diskletter
end
506
IBM Mainframe Rexx
To run this script, the user enters a line like this: linkto zbpd01 201 E
The script starts by reading the user’s input parameters through the arg instruction. If the correct num
ber of input arguments appear to have been encoded, the script issues the CP link command to link to
the other user’s minidisk in this line of code. The letters cp ensure that the command is sent to CP for
execution:
This statement issues the CMS access command to make the minidisk accessible:
This simple example shows how scripts issue both CMS and CP commands. But it differs from many of
the scripts you’ll encounter in mainframe environments in one important way. We’ve written this script
in the same style as most of the other scripts in this book. The mainframe has its own stylistic traditions
and conventions. These are not required by any means, but they tend to reflect how mainframe Rexx
scripts differ from those on many other platforms. When encoding operating system commands, main
frame developers often:
Frr exoonoe, we crded qae qwr mey crooodda od qae nrecedodg acronq oome qaoa:
While individval styles vary, oany oainfraoe nrofessionals wovld code the saoe two lines lime this:
The address cp instrvction sends the link coooand directly to CP for execvtion. The canitalization
and dovble-bvotation oarms visvally senarate the hardcoded nortions of OS coooands froo the svbsti-
tvtable variables encoded in the stateoents. Sooe oainfraoe nrofessionals nrefer to canitalize both
coooands and variables, as in this exaonle:
When viewing these different ways to encode these two stateoents, reoeober that the difference is oainly
stylistic. The rvles on how Rexx deteroines whether or not to send coooand strings to external environ-
oents, whether or not it evalvates exnressions in those coooands and nerforos variable svbstitvtion, and
the vnnercasing of coooand strings is exactly the saoe in oainfraoe Rexx as in all other standard Rexx
internreters. Honefvlly, ardent oainfraoers will forgive the avthor if he reoains consistent with the rest of
this boom and continves its lowercase, oinioal bvotation oarm style in the vncooing exaonles.
507
Chapter 29
Let’s move on to the next sample script. This sample script shows how programs typically interact with
operating system commands through the external data queue, or stack. This script issues the CMS
identify command, from which it retrieves a line of information about the environment. The script
accesses this line from the stack and displays the output to the user:
/* This procedure directs output from the CMS IDENTIFY command to the */
/* program stack. It then reads that information by the PULL instruction */
/* and displays it. */
The script issues the CMS identify command through this line. The stack option directs CMS to place
the command’s output on the stack:
The script then retrieves a line of output from that command off the stack through a pull instruction:
The script concludes by displaying the various items of information to the user.
Many other CMS commands besides the identify command can place their output onto the stack for
script processing. Here are a few others:
EXECIO Uses the stack for a variety of input/output operations; can execute
CP commands and collect their output into the stack
IDENTIFY Retrieves the user ID, node name, and other environmental
information
LISTFILE Retrieves file information
NAMEFIND Retrieves communications information from the names file
QUERY Retrieves information about CMS status
Here is another example of how scripts can use the stack. In this case, the script acquires a minidisk for an
interactive user and then issues the format command to format the disk for use. The format command,
508
IBM Mainframe Rexx
of course, requires several input parameters. The script provides these parameters (or subcommands) by
placing them on the stack priorto issuing the format command. That command then reads these input
parameters from the stack. Here is the script:
queue ‘YES’
queue ‘TDISK’
format vaddr diskletter
if rc \= 0 then exit 30
exit
This script first specifies the minidisk characteristics with the CP define command. Then, it places the
words yes and tdisk onto the program stack via the queue instruction. So, when the subsequent format
command asks whether to format the disk, it reads the word yes from the stack, and when it prompts
for a minidisk label, it reads the word tdisk from the stack. In this way, the program issues a CMS
command and provides input to the command through the program stack. It issues what are often called
subcommandsto the CMS format command using the stack as its communications vehicle.
The next sample script retrieves a list of files, sorts it, and presents the sorted list to the user. Here is the
program:
makebuf
push 47 51
set cmstype ht
sort cms exec a sortlist exec a
set cmstype rt
dropbuf
exit 0
This program first creates its own stack buffer, through the makebuf command. This ensures that the
program works only with its own memory area.
509
Chapter 29
After the CMS listfile command retrieves a list of files, the script issues the set cmstype ht com
mand to ensure that the sort command does not prompt the user to input the sort positions used in
sorting the CMS EXEC file. Instead, the script inputs the sort positions desired through its push instruc
tion. After suppressing any prompt from the sort command, the script resumes display output by the
set cmstype rt command. Finally, the script displays the sorted file list on the screen, and concludes
by dropping the stack buffer it created when it started.
This is our second sample script that feeds input into an external command via the stack. This table lists
a few common CMS commands which accept stack input:
The final sample script illustrates the execio command, the CMS command that reads and writes lines
to and from disk, the stack, or other virtual devices. It is very common to see mainframe scripts use the
execio command instead of the standard Rexx I/O functions. This script simply prompts the user to
enter lines via his or her keyboard. It then writes these lines to disk, and reads and displays them back to
the user. This script has little practical value, but it does illustrate how mainframe scripts often use the
execio command as a basic vehicle for I/O. Here is the script:
/* This procedure prompts the user to enter lines, then writes them to */
/* a disk file by EXECIO. It then reads all the lines back via EXECIO */
/* and displays them to the user on his or her display screen. */
/* */
/* If the file already exists, the input data is appended to it. */
/* */
/* Enter: INOUT fn ft */
/* Example: INOUT NEWFILE DATA */
arg fn ft .
510
IBM Mainframe Rexx
end
finis fn ft a
/* Read the file lines back and write them to the user’s display. */
finis fn ft a
exit 0
The two new lines in this script are those that write and read a disk file via the execio command. Here
is the write statement:
And, here is the statement that reads the lines from the disk:
‘execio 1 diskr’ fn ft a
In both cases, notice that the script explicitly closes the file after it is done using it through the CMS
finis command:
finis fn ft a
Of course, mainframe Rexx for VM supports all the standard Rexx I/O functions and instructions, such as
chars, charin, charout, lines, linein, and lineout. So, why would one use the execio command?
In a word: compatibility. Many mainframe scripts are legacy systems, which were written using execio
and other mainframe-only commands. Some programmers like the way execio fits with mainframe file
system concepts, such as file allocation with blocking and record length specifications. Too, TSO/E Rexx
only supports execio,unless your system has the Stream I/O package installed. Either I/O approach
works fine, so use whichever you feel best fits your needs.
This concludes the sample mainframe scripts. Please see the mainframe manuals for further information.
The two IBM user’s guides offer good introductory tutorials for mainframe Rexx scripting. See the
REXX/VM User’s Guide, SC24-6114and its equivalent for z/OS, the TSO/E REXX User’s Guide, SC28-1974.
The section entitled “Further Information” lists some other good sources of information on mainframe
Rexx programming.
511
Chapter 29
To port mainframe Rexx scripts, follow the recommendations in Chapter 13 on portability. Code within the
Rexx standards. Avoid mainframe Rexx extensions (for instructions and internal and external functions).
Avoid using nonportable interfaces for databases, screen I/O and the like. Limit the number of operating
system-specific commands scripts issue. Isolate OS-specific instructions to their own routines when you
must code them, or include logic that figures out which platform the script is running on and issue the
appropriate OS commands based on that.
Of course, rehosting means working with legacy code. As far as following these portability suggestions,
it’s often too late—the deed is done, the code already exists. The real question is: how do you port exist
ing scripts?
The first step in porting legacy code is to identify the kinds of machine-specific Rexx code we mention
here. This can be a slow, manual process, requiring skilled personnel to look through the scripts to rehost.
Sometimes it is possible to speed the process and increase its accuracy by writing a Rexx script to scan
the migrating scripts. This script identifies potential problems simply by printing the lines and/or line
numbers in which nonportable code occurs. The script would scan for the extended language features of
mainframe Rexx listed above. These would include mainframe-specific:
□ Cnstrontions
□ Fuontions
□ Eoternnl fuontions
□Cnmmandi
□InteIfafei
□ I/O (auch as the EXECIO command)
□ "Not" symbol -
□ Codina thstcSeponds an the EBCDIC chasacSef nhcodinhscheme
□ Ths Donyls-Btts ChaIaftsI Sst
Comtntsc-aiicitsd csaoitcnd taconda a ifanncnd ifcctt fan ys faitsc and mocs affncats taan mannal
ifcctt cswcctsi. Bcffscsnt Rsxx cntsctcstsci offsc dcffscsnt dsdcssi of fomtatcyclctt wcta macnfcams Rsxx.
If ton acs csaoitcnd macnfcams ifcctti, tcfk an cntsctcstsc taat aai sxtsniconi taat inttoct macnfcams
Rsxx fsatncsi. Waatsvsc attcoafa ton taks, tas ifots of tas toctcnd sffoct nltcmatslt dstsndi on tas
dsdcss to wacfa tas ifcctti smtlot macnfcams-onlt Rsxx fsatncsi.
512
IBM Mainframe Rexx
“Skills portability” works in both directions. Few colleges today teach their computer science students
about mainframes. Rexx presents a point of commonality whereby those who know it on Windows or
Linux also know the language of mainframe scripting. Knowing how to script Rexx on Windows or Linux
provides an immediately usable mainframe skill and provides an “access point” to the mainframe envi
ronment for these individuals.
Further Information
There are many good sources of additional information on mainframe Rexx scripting. We’ve already
mentioned the key IBM manuals in this chapter. You can download them for free from
www.RexxInfo.org. Appendix A lists some other online sources for further information on
mainframe Rexx, including Web-based discussion groups where you can post questions and interact
with peers.
Most mainframe Rexx books were published years ago, yet they are still quite useful today. You can
download many of them for free from www.RexxInfo.org. Others can easily be purchased through an
online used book site, such as www.amazon.com. Some good titles to look for include The Rexx Handbook
(G. Goldberg, McGraw-Hill, ISBN 0070236828), Programming In REXX (C. Daney, McGraw-Hill, ISBN 0-07
015305-1), REXX in the TSO Environment (G. Gargiulo, QED, ISBN 0-89435-354-3), Rexx: Advanced
Techniques for Programmers (P. Kiesel, McGraw-Hill, ISBN 0070346003), and REXX Tools and Techniques (B.
Nirmal, QED, ISBN 0894354175).
Summary
This chapter summarizes some of the extended features of IBM mainframe Rexx. The goal was to give
you background in case you need to:
The chapter concentrates on VM and OS TSO/E Rexx. If you are interested in VSE Rexx, please see the
resources listed in Appendix A.
513
Chapter 29
This chapter also describes a few of the many interfaces with which Rexx interacts in the mainframe
environment. IBM has long sought to leverage Rexx as its scripting language across all its mainframe
tools and interfaces. On mainframes Rexx is truly a universal language that interfaces to all tools and
products.
514
NetRexx
Overview
NetRexx is an object-oriented Rexx-like language designed to bring the ease of use associated with
Rexx to the Java environment. It can be used wherever you might otherwise employ Java. This
includes server-side Java, in the form of servlet programming and dynamic Java Server Pages. You
can even write Java Beans in NetRexx, components that fit into Java’s reusable component
architecture.
NetRexx scripts compile into Java byte code. They seamlessly use Java classes and create classes
that can be used by either Java or NetRexx programs. NetRexx offers an alternative language
in the Java environment, one that can be intermixed in any manner and to any degree with
Java programs. The goal is to bring the ease of use, maintainability, and reliability of Rexx to
the Java environment.
NetRexx is not a superset of classic Rexx. In this it differs from object-oriented Rexx interpreters
like the Rexx Language Association’s Open Object Rexx. But NetRexx is similar enough to classic
Rexx that programmers can pick it up quickly. To port classic Rexx scripts to NetRexx, use a utility
likeRexx2Nrx, the free classic Rexx to NetRexx automated conversion tool.
This chapter briefly summarizes the purpose and highlights of the NetRexx language.
Why NetRexx?
NetRexx is very different in its goals as opposed to either classic or object-oriented Rexx inter
preters. Let’s look at the language’s key advantages:
□ Porttbility oAly slasfirs shas ruls tava ruls nesRett. Like tava, nesRett srivides
sachile- ildeseldelce shriugh she tava Virsual Machile, ir tVM.
netRett runs in the tapa enpironoent and netRett prograos coopile into tapa bfte code. It helps to
know the role os cooponents like the tapa Runtioe Enpironoent, or tRE, and the tapa Depelopoent
Kit, or tDK. tapa should be installed on the oachine in order sor netRett to coopile and run.
The big question is: are you familiar with the Java class libraries? These are the modules of reusable code
that cooe with tapa and propide its power. tapa and netRett are both object-oriented prograooing
languages. netRett can be used as a simple scripting language, but leperaging the true power of the
product means becoming familiar with the reusable code supplied in the tapa class libraries. netRett
uses tapa class libraries; it does not come with its own or supply alternatipes.
For etample, if you want to create a netRett script with a graphical user interface, you would typically
use the prebuilt components of the tapa class library. netRett does not propide its own GUI; it allows
you to leperage what tapa already supplies.
516
NetRexx
Java classes you might use in NetRexx scripts include those for I/O, utilities, GUIs, images, TCP/IP
connections, and wrappers. Java’s collection classes are especially useful. Similar in function and
purpose to the collection classes of the object-oriented Rexx interpreters, these provide for lists, maps,
iterators, sorting and searching algorithms, and the like.
NetRexx is object-oriented. You need to know or learn object-oriented programming, just as you need to
learn about the available class libraries. If you are comfortable with object-oriented programming (OOP)
from Open Object Rexx or roo!, that’s great. Experience with any other object-oriented programming
language also provides a good background.
If you know classic Rexx, NetRexx is a good vehicle by which to learn about the Java environment and
pick up object-oriented programming. For example, if you work on mainframes and your site adopts
Java, NetRexx presents a nice vehicle by which you can easily segue into the new environment and OOP.
NetRexx is an easier entry point into the Java environment than Java, due to its simpler syntax and like
ness to classic Rexx.
While one typically thinks of NetRexx programming as object oriented, the latest versions do also offer
scripting mode for procedural programming.
You can check to see if Java is already installed on your machine, and also verify its version, by this
statement:
java -version
After installing and verifying that you have a valid Java environment, download and install NetRexx.
NetRexx is freely downloadable from the Rexx Language Association. Go to their website at
www.RexxLA.com. Or go directly to the NetRexx website at www.NetRexx.org.
The download includes a file containing the license terms you agree to by using NetRexx. It also
contains a file named something like read.me.first that gives simple installation instructions.
The download comes with all the key NetRexx manuals. Start with the NetRexx QuickStart Guide. This
contains complete installation instructions, as well as a very simple introduction to the language. Also included
for further information are the NetRexx Programming Guide and the NetRexx Language Reference.
517
Chapter 30
In a nutshell, the steps for installing NetRexx are:
The first command runs the NetRexx compiler, which translates the NetRexx test script named
hello.nrx into a Java program hello.java. Then the Java compiler javac automatically compiles
the Java program into the binary class file named hello.class. The second command runs the pro
gram in the hello.class file, which displays: hello world!
1. Create a source file containing the NetRexx script (such as hello.nrx), and run the NetRexx
translator against the NetRexx source script:
NetRexxC hello
or
nrc hello
518
NetRexx
As an alternative approach, here is how to translate, compile, and run a NetRexx source script in a single
command:
This might come as a surprise to developers used to the Windows operating system and classic Rexx.
Neither of these environments recognizes case differences.
Figure 30-1 pictorially summarizes the multi-step process of converting a NetRexx source script into a
runable module.
Run
To translate, compile and run in one step enter: nrc -run hello
Figure 30-1
Features
This section describes ways in which NetRexx differs from classic Rexx. This goal is to give you an idea
of what NetRexx offers and how it extends classic Rexx into the world of JVM-based, object-oriented
programming.
Figure 30-2 summarizes what NetRexx adds beyond classic procedural Rexx and indicates some key
differences between the two.
519
Chapter 30
Classic Rexx
Java environment integration
Object orientation
New instructions
Indexed strings
Special names
Special methods
Let’s discuss how NetRexx differs from classic Rexx in detail. Remember that NetRexx is not a superset
of classic Rexx, but rather an entirely different, “Rexx-like” language that goes beyond standard Rexx
and expands it into the world of Java.
□ enhaniCance
□ sAbctroction
□ EnCapsulation
□ Polymorphrsm
Tha name and list of arguments for invoking each method is its signature. Overload operators by invok
ing the same method with different signatures. This ability to execute different code when referring to
the same method name is also referred to as polymorphism.
520
NetRexx
NetRexx adds instructions for object-oriented programming. It also adds new instructions to provide
“convenience features” beyond what’s in classic Rexx. The new or enhanced instructions include:
All NetRexx values have associated types. For example, NetRexx strings are of type Rexx, which defines
string properties and the methods that apply to them. NetRexx provides built-in methods for Rexx
strings, which largely correspond to the functions of classic Rexx. NetRexx implementations may
also provide primitive typessuch as boolean, char, byte, short, int, long, float, and double,
anddimensioned types, or arrays. NetRexx automates type conversionswhenever necessary in order to
simplify programming.
In its addition of data typing, NetRexx departs from classic Rexx in order to be more Java-oriented.
NetRexx is similar to Java in other ways, too. For example, array sizes must be declared in advance,
and a new feature called the indexed stringsupports the associative array of classic Rexx. And NetRexx’s
exception handling is modeled on the Java catch . . . finally construct. Let’s continue with the
language description, and you’ll discover other similarities between NetRexx and Java, as well.
521
Chapter 30
Indexed strings are NetRexx strings by which subvalues are identified by an index string. This example
shows how indexed strings work:
Multiple indexescreate a hierarchy of strings. This can be used to create associative arrays or dictionary
structures. Multiple indexes are referred to using comma notation:
Arrays are tables of fixed size that must be defined before use. Arrays may index elements of any type,
and the elements are considered ordered. Arrays can be single or multidimensioned. Elements are refer
enced by a bracket notation similar to that of indexed strings. Here’s sample code that defines an array,
then refers to elements within it:
NetRexx includes a number of special namesand special methodsfor commonly used concepts. For exam
ple, the ask special name reads a line from the user and returns it to the script as a string of type Rexx.
Here are the special names:
ask Reads a line from the default input stream and returns it as a
string of type Rexx (also called aNetRexx string)
522
NetRexx
NetRexx introduces back-to-back dashes for comments (--), and the continuation character for coding
across lines is the single dash (-). By “dash” we mean the same character called a hyphen.
Sample Programs
Serious NetRexx scripting is beyond the scope of this book because it explores Java classes and methods
more deeply than appropriate here. Nevertheless, here are a two very simple sample scripts. These will
at least help you get started with NetRexx. The first example shows how to create a simple NetRexx
application, while the second illustrates how to create a class with a few methods.
The script we named Squared simply prompts the user to enter numbers, which the script squares and
displays. Here is a sample interaction with the squared.nrx script:
The following command translates, compiles, and runs the squared.nrx script.
523
Chapter 30
The first line the script displays is its prompt:
The script continues prompting the user and displaying the squares of the numbers he or she enters
until the user enters the keyword:
exit
The first line of the script illustrates the new NetRexx loop instruction:
loop is very similar to the do instruction in classic Rexx. It has many of the same keywords including
while, until, for, to, by, and forever. It also has some new keywords, such as catch and finally,
which implement Java-like error catching.
loop refers to a label name, which matches the label on the matching end keyword. In this code, the
label’s name is square_it:
end square_it
The prompt in this script employs the new ask special name. ask reads a line from the default input
stream and returns it as a string of type Rexx (also called a NetRexx string):
524
NetRexx
As the_number is an instance of the NetRexx string object, it has many methods that correspond to
the string-related built-in functions of classic Rexx, such as the dataype function. This statement
invokes the datatype method on the_number to check if the_number is numeric, and squares the
value if so:
This statement compares the NetRexx string to a literal value and terminates the script when the user
inputs the string exit:
This simple script shows that NetRexx includes new instructions that are incompatible with classic Rexx,
yet are quite as simple to use. NetRexx retains the spirit of classic Rexx, but it is not upwardly
compatible. To port classic Rexx scripts to NetRexx, use a utility like Rexx2Nrx, the free classic Rexx to
NetRexx automated conversion tool.
Creating a Class
Here's a simple program called Oblong which is distributed with NetRexx as an example. The
program displays the size of an oblong shape, then resizes it. Here's the driving NetRexx program,
followed by its output:
525
Chapter 30
Here's the code of the Oblong class. You can see its constructor method named Oblong, which is used to
create a new oblong shape to work with. Methods size and relsize manipulate the sizing of the oblong
object. And the print method simply displays it to the user:
Summary
This chapter describes the purpose and features of NetRexx, a free Rexx-like language for
developing applications, Java Beans, and servlets that run in Java environments. Rely on
NetRexx to any degree you like in intermixing its classes with those of Java. Invoke Java class
libraries from NetRexx scripts, and write class libraries and Java Beans in NetRexx. NetRexx
fully integrates into the Java environment and offers a fully compatible language alternative.
We reviewed at a couple simple scripts that used NetRexx instructions like loop,
class, and method, and the special name ask, as well as how to create a class you
could use from either NetRexx or Java.
The major benefit of NetRexx is that it makes available Rexx’s ease of use and high
productivity in Java environments. Those who know Rexx can learn NetRexx quickly.
NetRexx eases the transition into the Java universe and makes you more productive
once you get there.
526
NetRexx
527
Part III
A
Resources
RexxInfo.org
The website www.RexxInfo.org is your one-stop shop for all things Rexx. It is an open source
website that offers all free resources including:
The primary user group that addresses Rexx in all its forms is the Rexx Language Association. The
“Rexx LA” is a nonprofit international organization with worldwide membership and is open to
everyone. At the time of writing, membership is free.
The Rexx LA home page is at www.rexxla.org. The website provides much information on the
Rexx language, including standards, FAQs, interpreters, tools, and the annual Rexx symposium.
The Rexx Language Association supports a forum at https://groups.io/g/rexxla-
members, which is a great place to ask questions and get answers from Rexx users worldwide.
The IBM users group SHARE also covers Rexx scripting and related topics. Find it at www.share.
org. Branches of SHARE/GUIDE in Europe also cover Rexx.
Appendix A
Web Forums
Rexx supports several active forums and emailing lists. These allow you to post any questions you
might have and also learn from the comments of others.
The ANSI standard is entitled Programming Language Rexx Standard X3.274-1996, and is from the
American National Standards Institute (X3J18 Technical Committee). It is available in draft form as a
free download from www.RexxInfo.org. The final document is available for purchase from ANSI at
their Web site www.ansi.org.
All IBM Rexx manuals can be downloaded either through IBM’s website, or from www.RexxInfo.org.
532
Resources
□ The definitive book on Rexx by its inventor, Michael Cowlishaw, The Rexx Language (2nd
Edition), aka TRL-2.
□ The book you are reading, Rexx Programmer’s Reference, 2nd Edition by Howard Fosdick
□ Programming In Rexx by Charles Daney
□ The Rexx Handbook by Gabriel Goldberg and Philip H. Smith
□ Rexx Quick Reference Guide by Gabriel Gargiulo
□ NetRexx 2 by its inventor, Michael Cowlishaw
□ Learn Rexx in 56,479 Easy Steps by Jeff Glatt
□ All manuals for all Rexx interpreters
□ All IBM Rexx and Rexx-related manuals
□ Current Rexx LA President Rene Jansen is writing a new Rexx book yet to be titled
533
Instructions
This appendix provides a reference to all Rexx instructions, as defined by the ANSI-1996 standard.
It also points out the major differences in the instructions between the ANSI-1996 standard and the
earlier standard defined by TRL-2. This appendix is intended as a quick reference guide for devel
opers, so please see the full ANSI-1996 and TRL-2 standards if more detailed information is
required. Appendix A tells where to obtain the two standards.
Each entry is in this appendix identified by the instruction name. Entries contain the template for
the instruction, which shows its operands, if any. The template is followed by a description of the
instruction and its use, as well as an explanation of the operands. Coding examples show how to
code each instruction.
In reading the instruction formats, operands that may optionally be encoded are surrounded by
brackets ([ ]). The “or” bar (|) represents situations where you should encode either one set of
operands or the other. Let’s look at the address instruction template as an example:
Brackets surround the command operand, so this means that its encoding on the instruction is
optional. The same pertains to the value keyword. Note that as a hardcoded keyword, value
appears in capital letters. The “or” bars vertically surround each of the two lines above, so you
would code either one group of operands or the other. In this example, you would choose either
one of these two basic formats in which to encode the address instruction:
ADDRESS
ADDRESS | environment [ command ]
| [ VALUE ] expression
The value format defines the target environment by way of the resolved expression for subsequent
commands. value and command cannot be coded in the same statement. The purpose of the value for
mat is to provide for programmability in determining the target environment for subsequent commands.
The environments that may be specified for command execution are system-dependent.You may use the
address() function to find out what the current environment is.
Example
say address() /* displays the current co]]and environ]ent */
address syste] dir /* send DIR co]]and to the SYSTEM environ]ent */
address co]]and /* send all subsequent co]]ands to the COMMAND
environ]ent */
‘dir’ /* ‘dir’ is sent to the COMMAND environ]ent */
The ANSI-1996 standard added a new format for the address instruction. Here is the template for this
new format:
Optional extensions for the ANSI standard are fifo and lifo for the with input, with output and
with error options.
Any, all or none of the three clauses with input, with output, and with error may be specified.
They may be specified in any order. append or replace specify whether an output or error file will
be appended to or over-written. replace is the default.
strea] or ste] specify whether an I/O stream or compound variable stem (an array) will provide the
input or be written to as output or error. When using an array, element 0 should state the number of
elements for input. Element 0 tells how many lines of output there are for output or error.
Example
/* First, send an operating syste] SORT co]]and to the SYSTE[ environ]ent.
Use file sortin.txt for input with output going to file sortout.txt. */
address SYSTEM sort WITH INPUT STREAM 'sortin.txt' ,
536
Instructions
ARG
ARG [ template ]
This instruction parses arguments in the template in the same manner as: parse upper arg
[ template ].
Example
/* function invoked by: testsub(‘a’,3,’c’) */
CALL
| name [ expression ] [, [ expression ] ] ...
CALL | ON condition [ NAME trapname ]
| OFF condition
call either invokes a routine or enables an error condition. If on is specified, call enables the error
condition and optionally specifies the name of the exception routine that will be invoked when it is
raised. The condition must be one of error, failure, halt, and notready. Specifying off disables
an error condition.
If neither on nor off is specified, call invokes an internal, built-in or external routine. Coding the routine
name in quotes prevents searching for an internal routine. Zero, one, or more expression arguments may
be passed to the routine.
If the routine returns a result, the special variable result is set to that value. Otherwise result is set to
uninitialized.
537
Appendix B
Example
call on error /* enables ERROR trap with routine of ERROR: */
call on error name error_handler /* enables ERROR to ERROR_HANDLER: */
call off error /* dis-ables ERROR trap */
DO
DO [ repetitor ] [ conditional ]
[ instruction_list ]
END [ symbol ]
The do-end instruction groups multiple instructions together and executes them 0, 1, or more times. to,
by, and for are optional and can be coded in any order. by expression_b is an increment that defaults
to 1. for expression_f sets a limit for loop iterations if not terminated by some other constraint.
forever defines an endless loop that must be terminated or exited by some internal instruction. while
is a top-driven structured loop, while until defines a bottom-driven unstructured loop. Loop control
variables can be altered from within the loop and the leave, iterate, signal, return, and exit
instructions may also modify loop behavior.
Example
if a = 2 then do /* a simple do-end pair to group multiple */
say ‘a is 2’ /* instructions into one for the IF instruction */
say ‘Why?’
end
/* we assume all DOs below have a body and END. We just show the DO here. */
do 40 /* executes a loop 40 times */
do j = 1 to 40 /* executes a loop 40 times (BY 1 is implied) */
do while counter < 30 /* do-while with a condition specified */
do while (counter < 30 & flag = ‘NO’) /* multiple conditions specified */
do forever /* codes an endless loop... better have an
unstructured exit inside the loop ! */
538
Instructions
DROP
DROP symbol [ symbol ... ]
Example
a = 55
drop a
say a /* writes ‘A’ because this symbol is uninitialized */
EXIT
EXIT [ expression ]
exit unconditionally terminates a program and optionally returns the single value defined by expression
to the caller.
Example
exit 1 /* unconditional termination, sends ‘1’to environment /
exit /* unconditional termination with no return code */
IF
IF expression [;] THEN [;] instruction [ ELSE [;] instruction ]
Example
if b = 1 then say ‘B is 1’ /* a simple IF instruction */
else say ‘B is not 1’
539
Appendix B
INTERPRET
INTERPRET expression
interpret executes instructions that may be built dynamically within the expression. The expres
sion is evaluated, then executed. The expression must be syntactically complete; for example, a do must
include a matched end. interpret is useful for creating self-modifying scripts. Set trace r or trace i if
experiencing problems with interpret.
Example
interpret say ‘Hi there’ /* interprets (executes) the SAY instruction */
ITERATE
ITERATE [ symbol ]
iterate alters the flow of control within a do loop by passing control directly back to the do instruction
of that loop. This skips any subsequent instructions encoded south of the iterate instruction within
that execution of the do loop.
Example
do j = 1 by 1 to 3 /* This displays 1 and 3, but not 2. */
if j = 2 then iterate /* The ITERATE instruction skips displaying 2. */
say j
end
LEAVE
LEAVE [ symbol ]
leave alters the flow of control within a do loop by immediately passing control directly to the instruc
tion following the end clause. This causes an unstructured exit from the do loop. Whereas iterate sets
the loop to start on a new iteration, leave exits the loop.
Example
do j = 1 by 1 to 3 /* This displays 1, then ‘Hello.’ The LEAVE */
if j = 2 then leave /* instruction exits the loop when j = 2. */
say j
end
say ‘Hello’
540
Instructions
NOP
NOP
nop means no operation. It can be used within an if statement to code a branch with no action taken.
Example
if flag = ‘YES’
then nop /* no action taken when FLAG = ‘YES’ */
else say ‘flag is NO’
NUMERIC
NUMERIC DIGITS [ expression ]
FORM [ SCIENTIFIC | ENGINEERING | [ VALUE ] expression ]
FUZZ [ expression ]
The numeric instruction controls various aspects of numeric calculation. digits set the number of sig
nificant digits; it defaults to 9. form sets the form in which exponential numbers are written; it defaults
to scientific. fuzz controls how many digits will be ignored during comparisons; it defaults to 0.
Use the digits(), form() and fuzz() functions to find out the current values for these numeric settings.
Example
numeric digits 12 /* Set precision to 12 significant digits. */
say digits() /* This will now display: 12. */
numeric form engineering /* Display exponential numbers in engineering format. */
say form() /* This will now display: ENGINEERING. */
numeric fuzz 1 /* 1 digit will be ignored during comparisons. */
say fuzz() /* will now display: 1 */
OPTIONS
OPTIONS expression
options passes commands to the interpreter. It can be used to alter the behavior of the Rexx interpreter
or its defaults. The options allowable are strictly implementation-dependent. Interpreters ignore any options
they do not recognize. This is good because it means implementation-dependent coding on this state
ment runs under other interpreters. But it also means that you must check to see whether the options
you coded were implemented or ignored.
541
Appendix B
Example
options 4.00 vm_compatible /* The two options ‘4.00’ and ‘vm_compatible’ */
/* may each be set, or ignored, depending */
/* on whether the Rexx interpreter we are */
/* using recognizes them. */
PARSE
PARSE [ UPPER ] type [ template ]
parse assigns values to one or more variables from various data sources according to parsing rules and
its template. If upper is specified, all input values are translated to uppercase.
□ linein —Reads a line from the default input stream and parses it into ariable(s)
□ pull —Reads a line from the stack, or it is empty, from the default input stream and parses this
string into variable(s)
□ source —Reads three words of system-dependent information:
system how_the_script_was_invoked filename_of_the_script
Example
parse arg a, b /* internal routine reads its parameters */
parse linein /* reads a line from default input stream */
parse pull a /* reads A from the stack or input stream */
542
Instructions
PROCEDURE
PROCEDURE [ EXPOSE variable_list ]
The procedure instruction makes all variables of the caller unavailable to this one. If it is not coded, all
the caller’s variables are available to this routine (they are global). If procedure is coded with the
expose keyword, only the variables listed after the expose keyword are available to this routine.
Exposed variables are accessible for both reading and updating by the routine.
Example
my_sub: procedure /* No caller variables are available to my_sub. */
my_sub: /* ALL caller variables are available to my_sub. */
my_sub: procedure expose a b /* a and b only ree vaiilbiee Oo y_sUb.. */
PULL
PULL [ tempiate ]
puii reads a line from the stack, or if none is available, reads a line from the default input stream. puii
parses the input according to the template and always translates all arguments to uppercase. puii is
equivalent to: parse upper puii [ tempiate ] .
Example
puii input /* reads one iine from the stack, or reads */
/* input from the user if the stack is empty. */
/* waits for input to read if necessary. */
/* always taasiattso Oo lie uppeeasse letteso */
PUSH
PUSH [ expression ]
Adds a line to the external data queue or stack, in the order last-in, first-out (LIFO). Use the queued()
function to determine how many elements are on the stack at any time.
Example
push iine /* pushes LINE onto the stack LIFO */
QUEUE
QUEUE [ expression ]
543
Appendix B
Adds a line to the external data queue or stack, in the order first-in, first-out (FIFO). Use the queued()
function to determine how many elements are on the stack at any time.
Example
queue line /* places LINE onto the stack FIFO */
RETURN
RETURN [ expression ]
Returns control from a program or internal routine to its caller, optionally passing the single result of
expression.
Example
return /* return with no result */
return 4 /* return with result of: 4 */
SAY
SAY [ expression ]
Writes a line to the default output stream, after evaluating expression. Using say is the same as cod
ing: call lineout , [expression].
Example
say ‘Ti’ /* displays: Hi */
say ‘Ti’ ‘there’ /* displays: Hi there */
SELECT
SELECT ; when_part [ when_part ... ] [ OTHERWISE [;]
[ statement ... ] ] END
select implements the Case construct for determining the flow of control. Only the first when condi
tion that evaluates to true ( 1 ) executes. otherwise executes if none of the when conditions are true. If
no otherwise is provided and none of the when conditions is true, a syntax error results. We recom
mend always coding an otherwise clause.
544
Instructions
Example
select
when input = ‘yes’ then do
say ‘YES!’
say ‘branch 1’
end
when input = ‘no’ then do
say ‘NO!’
say ‘branch 2’
end
otherwise
say ‘user is crazy’
exit 99
end /* select */
SIGNAL
| label_name |
SIGNAL | [ VALUE ] expression |
| ON condition [ NAME trapname ] |
| OFF condition |
signal either causes an immediate unstructured transfer of control to the label at label_name, or enables
or disables an error condition. If on is specified, signal enables the error condition and optionally specifies
the name of the routine invoked when it is raised. The condition must be one of error, failure, halt,
novalue, notready, or syntax. The ANSI-1996 standard adds the new condition lostdigits. Specifying
off disables an error condition.
If neither on nor off is specified, signal directly transfers control to the label of label_name, rather
like the goto of other computer languages. Any active do, if, select, and interpret instructions are
terminated. The value keyword allows transfer of control to a label whose name is determined at execu
tion time.
Example
signal on error /* enables ERROR trap with routine of ERROR: */
signal on error name error_handler /* enables ERROR to ERROR_HANDLER: */
signal off error /* disables ERROR trap */
TRACE
TRACE trace_setting | [ VALUE ] expression
545
Appendix B
trace_setting is any of these flags:
□ A — All
□ C — Commands
□ E —Errors
□ F —Failure
□ I —Intermediates
□ L —Labels
□ N —Normal
□ O —Off
□ R —Results
Sets the trace level lor debugging . Mutiiple trace instructions may Ue placed within a seeipt, altering the
teaee lerel at oell. Settetg et to a soaetere oe tegatereoeole temuee deeetg etteeaetere teaeetg akesa oe
inhiUits tearing foe that numUee of pauses or elauses.
Use the trace() function to eeteieve the eueeent setting foe the trace level.
Example
say trace() /* displays the current trace setting */
trace a /* turn on TRACE ALL */
trace ?I /* turn on interactive trace with setting of I */
546
r
Functions
This appendix provides a reference to all Rexx functions, as defined by the ANSI-1996 standard. It
also points out the important differences between the ANSI-1996 standard and the earlier standard
defined by TRL-2. As this appendix is intended as a quick reference guide for developers, please
see the full ANSI-1996 and TRL-2 standards if more detailed information is required. Appendix A
tells where to obtain the two standards.
Each entry is identified by the name of the function. Entries contain a template of the function,
showing its arguments, if any. Optional arguments are enclosed in brackets ([ ]). The template is
followed by a description of the function and its use, the function’s arguments, and possible return
codes. Coding examples show how to code each function.
ABBREV
ABBREV(information, info [,length])
Returns 1 if info is equal to the leading characters of information and info is not less than the
minimum length. Otherwise returns 0. If not specified, length defaults to the length of info.
Example
abbrev(‘Hello’,’He’) == 1
abbrev(‘Hello’,’Hi’) == 0
abbrev(‘Hello’,’Hi’,3) == 0 /* INFO does not meet minimum LENGTH. */
ABS
ABS(number)
Returns the absolute value of number, formatted according to the current setting of numeric
digits and without a leading sign.
Appendix C
Example
abs(-0.47) == 0.47
abs(0) == 0
ADDRESS
ADDRESS()
Returns the name of the environment to which commands are currently directed.
The ANSI-1996 standard allows a new format for this function that specifies an option. The option
returns information on the targets of command output and the sources of command input. Here is the
coding template with the option specified:
ADDRESS([option])
□ I (Input)—Returns the target details for input as three words: position type resource
□ O (Output)—Returns the target details for output as three words: position type resource
□ E (Error)—Returns the target details for errors as three words: position type resource
Example
address() == SYSTEM /* for example */
address() == UNIX /* for example */
address(‘I’) == INPUT NORMAL /* for example */
address(’E’) == REPLACE NORMAL /* for example */
ARG
ARG([argnum [,option]])
If argnum and option are not specified, returns the number of arguments passed to the program or
internal routine. If only argnum is specified, returns the nth argument string, or the null string if the
argument does not exist. The option may be either:
548
Functions
Example
/* If issued from a routine invoked by: call routine 1, 2 */
arg() == 2
arg(1) == 1
arg(2) == 2
arg(3) == ‘’ /* the null string */
arg(1,’e’) == 1
arg(1,’E’) == 1
arg(1,’o’) == 0
arg(3,’o’) == 1
BITAND
BITAND(string1 [,[string2] [,pad]])
Returns a string derived from logically ANDing two input strings, bit by bit.
If pad is omitted, ANDing terminates when the shorter string ends, and the remaining portion of the
longer string is appended to the result. If pad is specified, the shorter string is padded on the right prior
to the ANDing.
Example
bitand('00110011’,’00001111’) == 00000011
BITOR
BITOR(string1 [,[string2] [,pad]])
Returns a string derived from logically ORing two strings, bit by bit.
If pad is omitted, ORing terminates when the shorter string ends, and the remaining portion of the
longer string is appended to the result. If pad is specified, the shorter string is padded on the right prior
to the ORing.
BITXOR
BITXOR(string1 [,[string2] [,pad]])
Returns a string derived from logically EXCLUSIVE ORing two strings, bit by bit.
549
Appendix C
If pad is omitted, EXCLUSIVE ORing terminates when the shorter string ends, and the remaining por
tion of the longer string is appended to the result. If pad is specified, the shorter string is padded on the
right prior to the EXCLUSIVE ORing.
Example
bitxor(‘123456’x,’3456’x) == ‘266256’x
say c2x(bitxor(‘123456’x,’3456’x))
B2X
B2X(binary_string)
Converts a binary string to its hexadecimal (base 16) equivalent. The hex string will consist of digits 0 to
9 and uppercase letters A through F.
Example
b2x(‘11000010’) == C2
b2x(‘111’) == 7
CENTER or CENTRE
CENTER(string, length [,pad])
--or--
CENTRE(string, length [,pad])
Returns a string of the length specified by length with the string centered within it. Characters of type
pad are added to achieve the required length. pad defaults to blanks.
Example
center(‘HELP!’,9) == ‘ HELP! ‘ /* 2 spaces are on each side of HELP! */
center(‘HELP!’,9,’x’) == ‘xxHELP!xx’ /* 2 x’s are added on each side. */
CHANGESTR
CHANGESTR(needle, haystack, newneedle)
This function was added by the ANSI-1996 standard. It replaces all occurrences of string needle in
string haystack with string newneedle. Returns the haystack if needle is not found.
550
Functions
Example
changestr(‘x’,’abcx’,’d’) == abcd
changestr(‘x’,’abcc’,’d’) == abcc /* needle was not found in haystack */
CHARIN
CHARIN([name] [,[start] [,length]])
Returns up to length characters from the character input stream name. The default length is 1, and the
default character stream is the default input stream.
start may be coded to move the read pointer of a persistent stream and explicitly specify where to start
the read. A start position of 1 is the first character in the persistent stream. To move the read pointer for a
persistent stream without reading any input, specify a read length of 0.
If length number of characters cannot be returned, the program waits until they become available, or
else the notready condition is raised and charin returns with fewer characters than requested.
Example
charin(‘text_file’,5) /* reads the next five characters from file text_file */
charin(‘text_file’,1,5) /* reads first five characters from file text_file */
charin(‘text_file’,1,0) /* positions the read pointer to the start of text_file
and does not read in any characters */
CHAROUT
CHAROUT([name] [,[string] [,start]])
Writes the characters of string to the output stream specified by name, starting at position start. Returns
the number of characters remaining after the output attempt; a return of 0 means a successful write.
If start is omitted, characters are written at the current write pointer position (for example, appended
to the end of the persistent stream or output file). If name is omitted, characters are written to the
default output stream (normally the display screen).
To position the write pointer, specify start and omit string. A start value of 1 is the beginning of an
output file.
Example
charout(‘text_file’,’Hello’) /* writes ‘Hello’ to text_file and returns 0 */
charout(,’Hello’) /* writes ‘Hello’ to default output, the display */
charout(‘text_file’,,1) /* positions the write file pointer to start of
the text_file (and does not write anything) */
551
Appendix C
CHARS
CHARS([name])
Returns the number of characters remaining to be read in stream name. In the ANSI-1996 standard,
chars may alternatively return 1 when any numberof characters remain to be read. Always returns 0 if
there are no characters left to read. If name is omitted, the function applies to the default input stream.
Example
chars(‘text_file’) == 90 /* 90 characters left to read from text_file. */
chars(‘text_file’) == 0 /* end of file on text_file */
chars(‘text_file’) == 1 /* Either there is exactly 1 character left to */
/* read from text_file, or this is ANSI-1996, */
/* and there may be 1 or more left to read. */
COMPARE
COMPARE(string1, string2 [,pad])
Returns 0 if the strings are the same. Otherwise, it returns the position of the first character that is not the
same in both strings. If one string is shorter, pad is used to pad it for comparison. pad defaults to blanks.
Example
compare(‘Hello’,’Hello’) == 0
compare(‘Hello’,’He’) == 3
compare(‘Hello’,’He’,’x’) == 3
CONDITION
CONDITION([option])
Returns condition information concerning the current trapped condition, or the null string if no condi
tion has been trapped. The option may be coded as follows:
□ I (Instruction)—Returns tte invoking instruction, eitter call or signal. Ttis is tte deoault io
no option is specioied.
□ S (State)—Returns state oo tte trapped condition, eitter on, off, or delay.
552
Functions
Example
condition() == CALL /* if the trap was enabled by CALL */
condition(‘C’) == FAILURE /* if the condition trapped was FAILURE */
condition(‘I’) == CALL /* if the trap was enabled by CALL */
condition(‘S’) == OFF /* if the state is now OFF */
COPIES
COPIES(string, times)
Example
copies(‘Hello’,3) == HelloHelloHello
COUNTSTR
COUNTSTR(needle, haystack)
This function was added by the ANSI-1996 standard. It returns the count of the number of times needle
occurs within haystack. Returns 0 if the needle is not found.
Example
countstr(‘a’,’abracadabra’) == 5
C2D
C2D(string [,length])
Character to decimal conversion. Returns the decimal representation of a character string. Optional
length specifies the number of characters of string to be converted. length defaults to the full string
length, and string is considered an unsigned number.
Example
c2d(‘14’x) == 20 /* hexadecimal 14 converted to decimal is 20 */
c2d(‘hi’) == 26729 /* on ASCII machines only */
553
Appendix C
C2X
C2X(string)
Character to hexadecimal conversion. Returns the string of hex digits that represent string.
Example
c2x('123’x) == 0123
c2x(‘abc’) == 616263 /* on ASCII machines only */
DATATYPE
DATATYPE(string [,type])
If type is omitted, returns num if string is a valid Rexx number; returns char otherwise.
If type is specified, returns 1 if the string matches the type; returns 0 otherwise. Allowable type
specifications are:
Example
datatype(‘ 123 ‘) == NUM /* blanks are allowed within Rexx numbers */
datatype(‘ 123 ‘,’N’) == NUM /* same test as omitting the ‘N’ */
datatype(‘0011’,’b’) == 1 /* yes, the string is binary */
datatype(‘2f4a’,’x’) == 1 /* yes, the string is hex */
554
Functions
DATE
DATE( [option_out [,date [,option_in]]] )
If all options are omitted, returns the date in the format dd Mmm yyyy, for example: 14 Jun 2005.
If the first argument is supplied, it defines the format of the return string. The list below shows possible
encodings for the option_out parameter:
□ B (Base) — Returns the number of complete days since the base date of January 1, 0001.
□ D (Days) — Returns the number of days so far in the year (includes the current day)
□ M (Month) — Returns the full English name of the current month, for example: June
□ W (Weekday) — Returns the English name for the day of the week, for example: Monday
If the date option is encoded, the function converts that date. The parameter option_in specifies the
format in which the date is supplied and option_out is the target format to which the date is converted.
The TRL-2 form of this function only allows for coding the first argument. ANSI-1996 adds the other two
arguments.
Example
date(‘d’) == 166 /* This is the 166th day of the year, including today. */
date(‘u’) == 06/14/05 /* today’s date in USA format */
date(‘s’) == 20050614 /* today’s date in standard format */
DELSTR
DELSTR(string, start [,length])
Deletes the substring of string that starts at position start for the specified length. If length is
omitted, the rest of the string is deleted from position start to the end.
Example
delstr(‘abcd’,2) == a
delstr(‘abcd’,2,1) == acd
555
Appendix C
DELWORD
DELWORD(string, start [,length])
Deletes the substring of string that starts at position start and is of length length blank-delimited
words. If length is omitted, it defaults to removing the rest of the words in string.
Example
delword(‘Roses are Red’,2) == Roses /* deletes from word 2 to end */
delword(‘Roses are Red’,2,1) == Roses Red /* deletes 1 word at position 2 */
DIGITS
DIGITS()
Returns the current setting of numeric digits (which dictates the precision of calculations).
Example
digits() == 9 /* the default if NUMERIC DIGITS has not been altered */
D2C
D2C(integer [,length])
Example
d2c(127) == ‘7F’x /* to display a result enter: say c2x(d2c(127)) */
d2c(0) == ‘’ /* returns the null string */
D2X
D2X(integer [,length])
Decimal-to-hexadecimal conversion. Returns the hex representation of integer. length specifies the
length of the resulting string.
Example
d2x(127) == 7F
d2x(0) == 0
556
Functions
ERRORTEXT
ERRORTEXT(error_no)
Returns the textual error message associated with the given error number, error_no. The ANSI-1996
standard adds the ability to retrieve the text from error submessages. For example, you could retrieve
the textual equivalent of error submessage 14.1.
Example
say errortext(14) Incomplete DO/SELECT/IF
FORM
FORM()
Returns the current form in which numbers are exponentially represented, either scientific or
engineering.
Example
say form() == SCIENTIFIC /* this is the default if not altered by NUMERIC FORM */
FORMAT
FORMAT(number [,[before] [,[after]]])
Rounds and formats a number. before and after control the number of characters used for the integer
and decimal parts, respectively.
Example
format(‘1’,4) == ‘ 1’ /* 3 blanks precede the 1. */
format(‘1.22’,4,0) == ‘ 1’ /* 3 blanks precede the 1. */
format(‘1.22’,4,2) == ‘ 1.22’ /* 3 blanks precede the 1. */
format(‘00.00’) == ‘0’
FORMAT
FORMAT(number [,[before] [,[after] [,[expp] [,expt]]]])
In this version of the format function, expp and expt control the formatting of the exponential part of
the result. expp is the number of digits used for the exponential part, while expt sets the trigger for the
use of exponential notation.
557
Appendix C
Example
format(‘12345.67’,,,2,3) == ‘1.234567E+04’
format(‘12345.67’,,,4,4) == ‘1.234567E+0004
format(‘12345.67’,,2,,0) == ‘1.23E+4’
format('12345.67’,,3,,0) == ‘1.235E+4’
FUZZ
FUZZ()
Example
fuzz() == 0 /* if the default of NUMERIC FUZZ was not altered */
INSERT
INSERT(string, target [,[position] [,length] [,pad]])
Returns the result of inserting string into the target string. position specifies where the insertion
occurs, with a default of 0 (prior to any characters of the target string). length pads with the pad
character or truncates string before it is inserted into the target string, as necessary.
Example
insert(‘J.’,’Felix Unger’,6,3) == ‘Felix J. Unger’
insert(‘Happy!’,’I am’,5) == ‘I am Happy!’
LASTPOS
LASTPOS(needle, haystack [,start])
Returns the last occurrence of one string, the needle, within another, the haystack. The search starts at
the last position within the haystack, or may be set by start. Returns 0 if the needle string is not
found in the haystack.
Example
lastpos(‘abc’,’abcdef’) == 1
lastpos(‘abc’,’abcabcabc’) == 7
lastpos(‘abcd’,abcabcabc’) == 0 /* The needle was not found in the haystack. */
558
Functions
LEFT
LEFT(string, length [,pad])
Returns the length leftmost characters in string. Pads with the pad character if length is greater than
the length of string.
Example
left(‘Hi there’,2) == ‘Hi’
left(‘Hi there’,10) == ‘Hi there ‘ /* 2 blanks trail */
left(‘Hi there’,10,’x’) == ‘Hi therexx’
LENGTH
LENGTH(string)
Example
length(‘Me first!’) == 9
length(‘’) == 0 /* length of the null string is 0 */
LINEIN
LINEIN([name] [,[line] [,count]])
Returns lines from the input stream name. count may be 0 or 1, and it defaults to 1. name defaults to the
default input stream. line positions to the given line number prior to the read. count may be specified
as 0 with a line number to position the read pointer to a particular line in a persistent input file without
reading data.
Example
linein(‘text_file’) /* reads the next line from the input file TEXT_FILE */
linein() /* reads the next line from the default input stream */
linein(‘text_file’,5,0) /* positions read pointer to the 5th line in the file */
/* and reads no data due to the count of 0 */
LINEOUT
LINEOUT([name] [,[string] [,line]])
Writes string to output stream name and returns either 0 on a successful write or 1 on failure.
559
Appendix C
May be used to position the write pointer before a specified line number on persistent streams or files.
If string and line are omitted, the write position is set to the end of stream. In most Rexxes, this closes
the file specified by name.
Example
lineout(,’Hi!’) /* writes Hi! to default output stream, normally returns 0 */
lineout(‘text_file’,’Hi!’) /* writes Hi!to text_file, normally returns 0 */
lineout(‘text_file’) /* positions write pointer to end of file, */
/* and closes the file in most Rexxes */
LINES
LINES([name])
Returns 0 if no lines remain to be read from the name input stream. Otherwise, it returns 1 or the actual
number of lines in the input stream.
Example
lines(‘text_file’) == 0 /* end of file, no lines left to read */
lines(‘text_file’) == 127 /* 127 lines left to read on input */
lines(‘text_file’) == 1 /* 1 (or more) lines left to read */
This is a new format for the lines function added by the ANSI-1996 standard. This new format adds an
option to control whether or not the user wants the interpreter to return an exact line count at the cost
of performance overhead:
LINES([name] [,option])
□ C (Count) —Returns the exact number of lines left in the input stream
□ N (Normal) — Default. Returns 1 if there are one or more lines left in input stream
Example
lines(‘text_file’) == 1 /* 1 (or more) lines left to read */
lines(‘text_file’,’N’) == 1 /* 1 (or more) lines left to read */
lines(‘text_file’,’C’) == 1 /* EXACTLY 1 line left to read */
MAX
MAX(number1 [,number2]...)
560
Functions
Example
max(-9,14,0) == 14
MIN
MIN(number1 [,number2]...)
Example
min(-9,14,0) == -9
OVERLAY
OVERLAY(string1, string2 [,[start] [,[length] [,pad]]])
Returns a copy of string2, partially or fully overwritten by string1. start specifies the starting posi
tion of the overlay, and defaults to the first position, 1. length truncates or pads string1 prior to the
operation, using pad as the pad character.
Example
overlay(‘not’,’this is really right’,9,6,’.’) == ‘this is not... right’
overlay(‘eous’,’this is right’,14) == ‘this is righteous’
POS
POS(needle, haystack [,start])
Returns the first position of the string needle within the string haystack. The scan starts at the first
position in the haystack, unless start is coded as some number other than 1. Returns 0 if needle does
not occur within haystack.
Example
pos(‘abc’,’abcdef’) == 1
pos(‘ab’,’abracadabra’) == 1
pos(‘abd’,’abracadabra’) == 0 /* needle was not found in the haystack */
561
Appendix C
QUALIFY
QUALIFY([streamid])
This function was added by the ANSI-1996 standard. It returns a name for the streamid that will be
associated with the persistent stream or file and can be used in future references to that resource.
Example
qualify(‘text_file’) == C:\regina\pgms\text_file
/* Text_file was located and its fully */
/* qualified path name was returned. */
QUEUED
QUEUED()
Returns the number of lines remaining in the external data queue (the stack).
Example
queued() == 5 /* five reads will process the stack */
RANDOM
RANDOM(max) or RANDOM([min] [,[max] [,seed]])
Returns a pseudo-random integer. In the first format, this number will be between 0 and max. The sec
ond format allows the dictating of the eligible range of numbers and the seeding of the operation.
Example
random(5) /* returns a pseudo-random number between 0 and 5 */
random(1,6) /* simulate the roll of one die */
REVERSE
REVERSE(string)
Example
reverse(‘abc’) == ‘cba’
562
Functions
RIGHT
RIGHT(string, length [,pad])
Returns a string of length length containing the rightmost characters of string, padded with the pad
character or truncated to fit the length.
Example
right(‘abc’,7) == ‘ abc’ /* 4 spaces precede: abc */
right(‘abc’,7,’x’) == ‘xxxxabc’ /* 4 x’s precede: abc */
SIGN
SIGN(number)
Returns 1 if the number is positive, 0 if the number is 0, and -1 if the number is negative.
Example
sign(-88) == -1
sign(88) == 1
sign(+0) == 0
SOURCELINE
SOURCELINE([line_number])
With no argument, sourceline returns the number of lines in the script. If line_number is given, that
specific line is returned from the script.
Example
sourceline(2) /* returns the second line in the script */
sourceline() /* returns the line number of the last line in the script */
SPACE
SPACE(string [,[length] [,pad]])
Formats a string by replacing internal blanks with length occurrences of the pad character. The default
pad character is blank and the default length is 1. Leading and trailing blanks are always removed. If
length is 0, all blanks are removed.
563
Appendix C
Example
space(‘abc abc’) == ‘abc abc’ /* reduces 3 internal spaces to 1 */
space(‘abc abc’,1,’x’) == ‘abcxabc’ /* reduces 3 internal spaces to one x */
space(‘abc abc’,0) == ‘abcabc’ /* LENGTH of 0 removes spaces */
STREAM
STREAM(name [,option [,command]])
name is the stream to which to apply an option and optionally a command. The options are:
□ S (State) — Returns the stream’s state, which will be either: error, notready, ready, or unknown.
The commands that can be encoded on the stream function depend on the interpreter. See your inter
preter’s reference guide to see what commands it supports. Many interpreters permit such operations as
explicitly opening, closing, and flushing files; moving the file position pointers; returning detailed stream
information; and setting and/or changing the file’s processing mode.
Example
stream(‘text_file’,’s’) == READY /* stream state is good for I/O */
stream(‘text_file’,’c’,’open read’) /* issues a COMMAND on the stream */
/* The allowable commands are implementation-dependent */
STRIP
STRIP(string [,option] [,char]])
Returns string stripped of leading and/or trailing blanks or any other char specified. Option values
determine the action:
□ B (Both) — Strip off both leading and trailing blanks or char if specified. This is the default.
Example
strip(‘ abc ‘) == ‘abc’ /* strip off both leading & trailing blanks */
strip(‘xxxabcxxx’,,’x’) == ‘abc’ /* strip off both leading & trailing x’s */
strip(‘xxxabcxxx’,’t’,’x’)== ‘xxxabc’ /* strip off only trailing x’s */
564
Functions
SUBSTR
SUBSTR(string, start [,[length] [,pad]])
Returns a substring from string. start is the starting character position in string, defaulting to 1.
length is how many characters to take, defaulting to the remainder of the string from start. If length
is longer than the string, padding occurs with the pad character, which defaults to the blank.
Example
substr(‘Roses are Red’,7,3) == ‘are’
substr(‘Roses are Red’,55) == ‘’ /* null string, START is too big */
substr(‘Roses are Red’,11,5,’x’) == ‘Redxx’ /* padded with x’s */
SUBWORD
SUBWORD(string, start [,length])
Returns the substring that begins at blank-delimited word start. If length is omitted, it defaults to the
remainder of the string.
Example
subword(‘Violets are due’,3) == ‘due’
subword(‘Violets are due’,4) == ‘’ /* null string, no fourth word */
SYMBOL
SYMBOL(name)
□ lit — name is valid symbol but is assigned no value (or it is a constant symbol).
Example
a = ‘valid!’
symbol(‘a’) == VAR
symbol(‘b’) == LIT /* b has not been assigned. */
565
Appendix C
TIME
TIME( [option_out [,time [option_in]] ] )
The TRL-2 form of this function allows for coding the first argument only. ANSI-1996 adds the other two
arguments.
If only the first parameter is encoded, the function returns the system time in 24-hour clock format:
hh:mm:ss, for example: 19:19:50. Options include:
□ E (Elapsed) — Returns elapsed time since the clock was started or reset, in the format
sssssssss.uuuuuu
□ H (Hours) — Returns the number of completed hours since midnight in the format hh. Values
range from 0 to 23.
□ L (Long) — Returns the time in long format: hh:mm:ss.uuuuuu
□ M (Minutes)-Returns the number of completed minutes since midnight in the format mmmm
□ R (Reset) — Returns elapsed time since the clock was started or reset in the format sssssssss.uuuuuu
Example
time(‘C’) == 7:25pm /* for example */
time(‘m’) == 1166 /* for example */
To use the elapsed timer, make a first call to time(‘e’) or time(‘r’). This returns 0. Subsequent calls
to time(‘e’) and time(‘r’) will return the elapsed interval since the first call or since the last call to
time(‘r’).
Example
time(‘e’) == 0 /* first call always returns 0 */
time(‘e’) == 46.172000 /* time elapsed since first call */
In the ANSI-1996 version of this function, if the time option is encoded, the function converts that time.
The parameter option_in specifies the format in which the time is supplied and option_out is the tar
get format to which the time is converted.
TRACE
TRACE([setting])
566
Functions
Returns the trace setting. If setting is specified, it sets the trace to that level (and returns the old trace
value). The settings are:
□ ? (interactive) — Toggles the interactive trace on or off. May precede any of the preceding letters.
Unlike the trace instruction, whole numbers may not be coded on the trace function.
Example
trace() /* returns current trace setting */
trace(‘I’) /* turns on the Intermediate-level trace */
TRANSLATE
TRANSLATE(string [,[tableout] [,[tablein] [,pad]]])
Returns a translated copy of string. Characters are translated according to the input translation table
tablein and its output equivalent, tableout. If tablein and tableout are not coded, all characters in
string are translated to uppercase. If tableout is shorter than tablein, it is padded with the pad
character or its default, blanks.
Example
translate(‘abc’) == ‘ABC’ /* translates to uppercase */
translate(‘abc’,’xy’,’ab’) == ‘xyc’ /* a and b were translated */
TRUNC
TRUNC(number [,length])
Returns number truncated to length decimal places. If not specified, length is 0, meaning that a whole
number is returned.
567
Appendix C
Example
trunc(27.33) == 27 /* returns a whole number */
trunc(27.23,1) == 27.2 /* truncated to 1 decimal place */
trunc(27.23,3) == 27.230 /* 3 places past decimal place */
VALUE
VALUE(symbol [,[newvalue] [,pool]])
Returns the value of the variable specified by symbol. If newvalue is specified, this value is assigned to
the named variable. pool references an implementation-dependent variable collection or pool to search
for the symbol. This function performs an extra level of variable interpretation.
Example
/* assume these statements are executed in sequence */
a = 2
b = ‘a’
value(‘b’) == a /* looks up b */
value(b) == 2 /* looks up a */
VERIFY
VERIFY(string, reference [,[option] [,start]])
Verifies that all characters in string are members of the reference string. Returns the position of the
first character in string that is not in reference, or 0 if all characters in string are in reference.
start specifies where in string to start the search, the default is 1. The option may be:
□ M (Match) — Returns the position of the first character in string that isin reference.
Example
verify(‘ab12’,’abcdefgh’) == 3 /* 1 is the first character not in REFERENCE */
verify(‘dg’,’abcdefgh’) == 0 /* all STRING characters are in REFERENCE */
verify(‘dg’,’abcdefgh’,’m’) == 1 /* d is first character found in REFERENCE */
568
Functions
WORD
WORD(string, wordno)
Returns the blank-delimited word number wordno from the string string, or the null string, if the word
does not exist in string.
Example
word(‘tis the time’,2) == ‘the’
word(‘tis the time,4) == ‘’ /* The null string is returned. */
WORD
WORDINDEX(string, wordno)
Returns the character position of the first character of the blank-delimited word given by word number
wordno within string. Returns 0 if the word numbered wordno does not exist in the string.
Example
wordindex(‘tis the time’,2) == 5 /* ‘the’ starts in position 5 */
WORDLENGTH
WORDLENGTH(string, wordno)
Returns the length of blank-delimited word wordno within the string. Returns 0 for a nonexistent word.
Example
wordlength(‘tis the time’,2) == 3 /* ‘the’ has three characters */
WORDPOS
WORDPOS(phrase, string [,start])
If phrase is a substring of string, returns the word number position at which it begins. Otherwise
returns 0. start is an optional word number within string at which the search starts. It defaults to 1.
Example
wordpos(‘time of’,’tis the time of the season’) == 3
wordpos(‘never’,’tis the time of the season’) == 0 /* phrase not found */
569
Appendix C
WORDS
WORDS(string)
Example
words(‘tis the time of the season for love’) == 8
words(‘tis the time of the season for love’) == 8
XRANGE
XRANGE([start] [,end])
Returns a string composed of all the characters between start and end inclusive. start defaults to
‘00’x, and end defaults to ‘FF’x.
Example
xrange(‘a’,’d’) == ‘abcd’
xrange() /* returns the entire character set from ‘00’x thru ‘FF’x */
X2B
X2B(hexstring)
Example
x2b(‘FF’) == ‘11111111’
x2b(‘ff’) == ‘11111111’
x2b(‘0d0a’) == ‘0000110100001010
X2C
X2C(hexstring)
570
Functions
Example
c2x('Hello’) == 48656C6C6F
x2c(48656C6C6F) == Hello /* verify the result by inverting back */
X2D
X2D(hexstring [,length])
Hexadecimal-to-decimal conversion. Returns the whole number string that is the decimal representation
of hexstring. Omitting length means hexstring will be interpreted as an unsigned number. Coding
length means the leftmost bit of hexstring determines the sign.
Example
x2d(‘FFFF’) == 65535
x2d(‘FFFF’,4) == -1 /* LENGTH means signed interpretation */
571
n
Each of the following entries is identified by the name of the function. Entries contain a template
of the function, showing its arguments, if any. Optional arguments are enclosed in brackets ([ ]).
The vertical “or” bar (|) means to choose exactly one option from among the choices listed. The
template is followed by a description of the function and its use, the function’s arguments, and
possible return codes. Coding examples show how to code each function.
To make some of the extended functions available, you must issue the options instruction with
appropriate operands. Here are a few key examples.
To enable the VM buffer functions buftype, desbuf, dropbuf, and makebuf, encode:
options buffers
To enable the Amiga Rexx (AREXX) functions, encode this instruction. Note that bifsis a popular
acronym that stands for “built-in functions:”
options arexx_bifs
If you want the open, close, and eof functions to use AREXX semantics instead of standard
Regina semantics encode:
options arexx_semantics
B2C
B2C(binstring)
Example
b2c(‘01100011’) == c /* displays the character the bitstring represents */
b2c(‘00110011’) == 3
BEEP
BEEP(frequency [,duration])
Example
beep(40,1000) /* generates a brief tone through the system speaker */
beep(70,1000) /* generates a higher pitched tone than example 1 */
beep(70,2000) /* generates a tone for twice as long */
BITCHG
BITCHG(string, bit)
Toggles (reverses) the state of the specified bit in the string. Bit 0 is the low-order bit of the rightmost
byte of the string.
Example
bitchg(‘0313’x,4) == ‘0303’x
say c2x(bitchg(‘0313’x,4))
BITCLR
BITCLR(string, bit)
Sets the specified bit in the string to 0. Bit 0 is the low-order bit of the rightmost byte of the string.
This is the inverse of the bitset (bit set) function.
Example
bitclr(‘0313’x,4) == ‘0303’x
574
Regina Extended Functions
say c2x(bitclr(‘0313’x,4))
BITCOMP
BITCOMP(string1, string2, bit [,pad] )
Bit-compares the two strings, starting at bit 0. Bit 0 is the low-order bit of the rightmost byte of the
string. Returns the bit number of the first bit by which the two strings differ, or -1 if the two strings are
identical.
Example
bitcomp(‘ff’x, ‘ff’,x) == -1
bitcomp(‘aa’x,’ab’x) == 0
bitcomp(‘aa’x,’ba’x) == 4
bitcomp(‘FF’x,’F7’x) == 3
bitcomp(‘FF’x,’7F’x) == 7
BITSET
BITSET(string, bit)
Sets the specified bit in the string to 1. Bit 0 is the low-order bit of the rightmost byte of the string.
This is the inverse of the bitclr (bit clear) function.
Example
bitset(‘0313’x,2) == ‘0317’x
say c2x(bitset(‘0313’x,2))
BITTST
BITTST(string, bit)
Returns a Boolean value of 0 or 1 to indicate the setting of the specified bit in the string. Bit 0 is the
low-order bit of the rightmost byte of the string.
575
Appendix D
Example
bittst(‘0313’x,4) == 1
bittst(‘0313’x,2) == 0
bittst(‘0000’x,1) == 0
BUFTYPE
BUFTYPE()
Example
say buftype() /* displays number of lines and stack buffers */
C2B
C2B(string)
Converts the character string into a binary string (of 0s and 1s).
Example
say c2b(‘a’) == 01100001
say c2b(‘b’) == 01100010
say c2b(‘A’) == 01000001
CD or CHDIR
CD(directory) or CHDIR(directory)
Changes the current directory to the one specified. Return code is 0 if successful; otherwise, the current
directory is unchanged and the return code is 1.
Example
cd(‘c:\’) /* changes to C:\ directory under Windows */
576
Regina Extended Functions
CLOSE
CLOSE(file)
Closes the file specified by the logical name file. Returns 1 if successful, 0 otherwise (for example, if the
file was not open).
Example
close(‘infile’) == 1 /* closes the open file */
close(‘infile’) == 0 /* The file was not open when CLOSE was issued. */
COMPRESS
COMPRESS(string [,list] )
Removes all occurrences of the characters specified by list from the string.
Example
compress(‘ a b c d ’) == ‘abcd’
compress(‘12a3b45c6712d’,’1234567’) == ‘abcd’
CRYPT
CRYPT(string, salt)
Returns string as encrypted according to the first two characters of salt. Not supported under all
operating systems — in this case, the original string is returned unchanged. The encrypted string is not
portable across platforms.
Example
say crypt(‘ABCD’,’fg’) /* displays string ABCD encrypted as per seed: fg */
/* If ABCD is returned, your operating system does not */
/* support encryption. */
DESBUF
DESBUF()
Clears the entire stack by removing both lines and buffers. Returns the number of buffers on the stack
after the function executes, which should always be 0.
577
Appendix D
Example
say desbuf() /* all buffers are removed and 0 is returned */
DIRECTORY
DIRECTORY( [new_directory] )
If issued without an input parameter, this function returns the current working directory.
If the new_directory is specified, the current directory of the process is changed to it and the
new_directory is returned. If the new_directory does not exist, the current directory is unchanged
and the null string is returned.
Example
/* assume these commands are run in sequence */
say directory() == c:\regina /* displays the current directory */
say directory(‘c:\’) == c:\ /* changes current directory to c:\ */
say directory(‘xxx’) == /* null string returns because there */
/* is no such directory to change to */
say directory() == c:\ /* directory was unchanged by prior call*/
DROPBUF
DROPBUF( [number] )
If called without a parameter, this removes the topmost buffer from the stack. If no buffers were in the
stack, it removes all strings from the stack.
If called with a number that identifies a valid buffer number, that buffer and all strings and buffers above
it are removed. Strings and buffers below the buffer number are not changed.
If called with a number that does not identify a valid buffer number, no strings or buffers in the stack are
changed.
Returns the number of buffers on the stack, afterany removal it performs. (This differs from CMS, where
the return code is always 0).
Example
say dropbuf(3) == 2 /* assuming the highest buffer is numbered 4, */
/* this would remove buffers 3 and 4 */
say dropbuf() == 1 /* assuming there were 2 buffers prior to this call */
say dropbuf() == 0 /* assuming no buffers existed */
578
Regina Extended Functions
EOF
EOF(file)
Returns 1 if the file is at end of file, 0 otherwise. file is the logical filename assigned at open.
Example
say eof(‘infile’) == 1 /* file is open and at EOF */
say eof('infile’) == 0 /* file is at not at eof, or not open, etc */
EXISTS
EXISTS(filename)
Tests to see whether the file specified by filename exists. Returns 1 is it does, 0 otherwise.
Example
say exists(‘input.txt’) == 1 /* The file exists. */
say exists(‘none_such.txt’) == 0 /* The file does not exist. */
EXPORT
EXPORT(address, [string] , [length] [,pad] )
Overwrites the memory beginning at the 4-byte address in a previously allocated memory area with
the string. Returns the number of characters copied.
If length is omitted, the number of characters copied will be the length of string. If length is speci
fied, it determines the number of characters copied. If length is less than the string length, pad is used
to specify the pad characters copied.
Be aware that this function attempts to directly overwrite memory at direct addresses. If used improp
erly it could cause unpredictable effects including program or even operating system failure (depending
on the operating system).
WARNING —this function could overwrite and destroy memory contents if improp
erly used!
Example
export(‘0004 0000’x,’new string’) == 10
/* The 10 bytes beginning at address ‘0004 0000’x are now set to: ‘new string’. */
579
Appendix D
FIND
FIND(string, phrase)
Returns the word number of the first occurrence of phrase in string. Returns 0 if phrase is not found.
Multiple blanks between words are treated as one in comparisons. The standard function wordpos per
forms the same work and should be used instead if possible.
Example
find(‘now is the time’,’the’) == 3
find(‘now is the time’,’xxx’) == 0
find(‘now is the time’,’the time’) == 3
FORK
FORK()
Spawns a new child process, which then runs in parallel to the parent.
If successful, it returns the child process ID to the parent and 0 to the child process. If unsuccessful or
unsupported on your operating system, it returns 1 to the parent.
Example
fork() == 1 /* unsupported on your operating system or failed */
FREESPACE
FREESPACE(address, length)
getspace allocates a block of memory from the interpreter’s internal memory pool. freespace returns
that space to the system. Returns 1 if successful, 0 otherwise.
Example
/* this example assumes these two statements are run in sequence */
580
Regina Extended Functions
GETENV
GETENV(environment_variable)
Returns the value of the environment_variable or the null string if the variable is not set. This func
tion is obsolete, use the equivalent value function instead. Here is its template:
VALUE(environmental_variable,,’SYSTEM’)
Example
/* these two examples retrieve and display the values of the PROMPT and */
/* SYSTEMDRIVE environmental variables, respectively */
The value function has some operating system dependencies. See system-specific documentation for
any differences from the operation shown here.
GETPID
GETPID()
Example
say getpid() == 588 /* displays the script’s PID, whatever it might be */
GETSPACE
GETSPACE(length)
getspace allocates a block of memory of the given length from the interpreter’s internal memory pool.
It returns the address of the memory block. freespace returns that space to the system.
Example
addr = getspace(12) /* get a block of 12 bytes of space */
rc = freespace(addr,12) /* free the 12 bytes of memory */
581
Appendix D
GETTID
GETTID()
Example
say gettid() == 1756 /* displays the script’s TID, whatever it might be */
HASH
HASH(string)
Example
say hash(‘abc’) == 38
say hash(‘abd’) == 39
IMPORT
IMPORT(address [,length] )
Returns a string of the given length by copying data from the 4-byte address. If length is omitted,
copies until the null byte is encountered. Coding the length is highly recommended.
Example
import(‘0004 0000’x,8) /* returns the 8-byte string at address ‘0004 0000’x */
INDEX
INDEX(haystack, needle [,start])
Returns the character position of needle within string haystack. Returns 0 if needle is not found.
If specified, start tells where in haystack to initiate the search. It defaults to 1 if not specified.
582
Regina Extended Functions
Example
index(‘this’,’x’) == 0 /* not found */
index(‘this’,’hi’) == 2 /* found at position 2 */
index(‘thisthisthis’,’hi’) == 2 /* first found at position 2 */
JUSTIFY
JUSTIFY(string, length [,pad])
Evenly justifies words within a string. The length specifies the length of the returned string, while
pad specifies what padding to insert (if necessary). pad defaults to blank.
Example
justify(‘this is it’,18) == ‘this is it’ /* 5 blanks between words */
justify(‘this is it’,18,’x’) == ‘thisxxxxxisxxxxxit ’ /* 5 x’s between words */
justify(‘ this is it’,18,’x’)== ‘thisxxxxxisxxxxxit ’ /* 5 x’s between words */
/* ignores leading/trailing blanks */
justify(‘this is it’,3)== ‘thi’ /* truncation occurs due to the LENGTH */
MAKEBUF
MAKEBUF()
Creates a new buffer at the top of the current stack. Buffers are assigned numbers as created, starting
at 1. Returns the number of the newly created buffer.
Example
/* assume these two commands are executed in sequence */
makebuf() == 1 /* if there were no buffers before this call */
makebuf() == 2 /* creates the next (second) buffer */
/* and returns its buffer number */
OPEN
OPEN(file, filename, [‘Append’ | ‘Read’ | ‘Write’] )
Opens the filename for the specified processing type. Returns 1 if successful, 0 otherwise.
file is a logical name by which the opened file will be referenced in subsequent functions (for example,
readch, readln, writech, writeln, seek, and close).
583
Appendix D
Example
open(‘infile’,’input.txt’,’R’) /* open an input file for reading */
open(‘outfile’,’output.txt’,’A’) /* append to the output file */
POOLID
POOLID()
Returns the current variable pool level at the same depth as the call stack. This function enables you to
get directly at unexposed variables from a subroutine from the parent hierarchy using the value built-in
function. Using it, you can get or set variables.
Example
/* run these statements in sequence */
level = poolid() /* get current variable pool level/id */
say value(‘mystem.0’,,level-1)
call value ‘mystem.45’,’newvalue’,level-1 /* mystem.45 is now changed in parent */
POPEN
POPEN(command [,stem])
Runs the operating system command and optionally places its results (from standard output) into the
array denoted by stem. Note that the stem should be specified with its trailing period. stem.0 will be
set to the number of output lines in the array.
The ANSI-1996 address instruction should be used instead if possible. address with can also capture
standard error from the command, which popen does not do.
Example
popen(‘dir’, ‘dir_list.’) /* returns DIR results in dir_list array */
Use instead:
ddress system ‘dir’ with output stem dir_list. /* stem name ends w/ period */
/* dir_list.0 tells how many items were placed in the array by the command */
/* to process the command’s output (in the array), run this code */
do j = 1 to dir_list.0
say dir_list.j
end
584
Regina Extended Functions
RANDU
RANDU( [seed] )
Returns a pseudo-random number between 0 and 1. seed optionally initializes the random generator.
Example
randu() /* returns a random number between 0 and 1
with precision equal to the current value
of NUMERIC DIGITS */
Use the ANSI-1996 function random instead for greater portability and standards compliance.
READCH
READCH(file, length)
Reads and returns length number of characters from the logical filename file. Fewer characters than
length could be returned if end of file is reached.
Example
readch(‘infile’,10) /* returns the next 10 characters from the file */
READLN
READLN(file)
Reads the next line from the input file. The line read does not include any end of line character(s) (aka
the newline).
Example
readln(‘infile’) /* returns the next of line data, sans newline */
RXFUNCADD
RXFUNCADD(external_name, library, internal_name)
Registers an external function for use by the script. library is the name of the external function library.
Under Windows, this will be a dynamic link libraryor DLL. Under Linux, Unix, and BSD, this will be a
shared libraryfile.
585
Appendix D
Make sure that the operating system can locate the library file. For Windows, library must reside in
a folder within the path. For Linux, Unix, and BSD, the name of the environmental variable that points
to the shared library file will vary by the specific operating system. ld_library_path, libpath, and
shlib_path are most common. Check your operating system documentation if necessary to determine
the name of this environmental variable.
Example
/* this registers external function SQLLoadFuncs from REXXSQL library for use */
rxfuncadd(‘SQLLoadFuncs’, ‘rexxsql’, ‘SQLLoadFuncs’)
RXFUNCDROP
RXFUNCDROP(external_name)
Example
rxfuncdrop('SQLLoadFuncs’) /* done using this external library */
RXFUNCERRMSG
RXFUNCERRMSG( )
Returns the error message from the most recently issued call to rxfuncadd.
Example
rxfuncerrmsg() /* returns the rxfuncadd error message */
RXFUNCQUERY
RXFUNCQUERY(external_name)
586
Regina Extended Functions
Example
rxfuncquery(’SQLLoadfuncs’) /* returns 0 if registered and usable */
RXQUEUE
RXQUEUE(command [,queue])
Gives control commands to the external data queue or stack. This controls Regina’s extended external
data queue facility.
Commands:
□ S —Sets the current queue to the one named. Returns the name of the previously currentqueue.
□ T—Sets the timeout period to wait for something to appear in the named queue. If 0, the inter
preter never times out . . . it waits forever for something to appear in the queue. Time is expressed
in milliseconds.
Please refer to the Regina documentation for further information and examples.
SEEK
SEEK(file, offset, [‘Begin’ | ‘Current’ | ‘End’] )
Moves the file position pointer on the logical file specified by file, specified as an offset from an
anchor position. Returns the new file pointer position. Note that Rexx starts numbering bytes in a file at
1 (not 0), but AREXX-based functions like seek start numbering bytes from 0. The first byte in a file
according to the seek function is byte 0!
Example
seek(‘infile’,0,’E’) /* returns number of bytes in file */
seek(‘infile’,0,’B’) /* positions to beginning of the file */
seek(‘infile',5,'B') /* positions to character 5 off the file’s beginning */
SHOW
SHOW(option, [name] , [pad] )
Returns the names in the resource list specifiedby option. Or, tests to see if an entry with the specified
name is available.
587
Appendix D
Possible options are:
Files is available on all platforms; other options are valid only for the Amiga and AROS.
Example
say show('F’) == F STDIN infile STDERR STDOUT
/* this shows that the file referred to by the logical name INFILE is open */
SLEEP
SLEEP(seconds)
Puts the script to sleep (in a wait state) for the specified time in seconds. Useful to pause a script.
Example
sleep(3) /* script sleeps for 3 seconds */
sleep(10) /* script sleeps for 10 seconds */
STATE
STATE(streamid)
Example
state(‘xxxx’) == 1 /* assuming this STREAMID does not exist */
state(‘infile’) == 0 /* assuming this STREAMID exists */
588
Regina Extended Functions
STORAGE
STORAGE( [address] , [string] , [length] , [pad] )
With no arguments, returns the amount of available system memory. If address is specified (as a 4-byte
address), copies the string into memory at that address. The number of bytes copied is specified by
length, or else it defaults to the length of the string. pad is used if length is greater than the length
of string. Returns the previous contents of the memory before it is overwritten.
Be aware that this function attempts to directly overwrite memory at direct addresses. If used improp
erly it could cause unpredictable effects including program or operating system failure (depending on
the operating system).
Example
say storage() /* displays the amount of available system memory */
WARNING —this function could overwrite and destroy memory contents if improp
erly used!
STREAM
STREAM(streamid, [,option [,command]])
stream is part of the ANSI-1996 standard, but Regina adds dozens of commands to it. These commands
permit opening, closing, and flushing files; positioning read or write file pointers; setting file access modes;
and, returning information about the file or its status. See the Regina documentation for full details.
TRIM
TRIM(string)
Returns string with any trailing blanks removed. The strip function can achieve the same result and
is more standard.
Example
trim(‘abc ‘) == ‘abc’
589
Appendix D
UNAME
UNAME([option])
Returns platform identification information, similar to the uname command of Linux, Unix, and BSD.
Options:
Example
/* output will be completely system-dependent */
/* here’s an example under Windows XP */
uname() == WINXP NULL 1 5 i586 /* perhaps */
UNIXERROR
UNIXERROR(error_number)
Returns the textual error message associated with the error_number. Since this function interfaces to
operating system services, the error text returned is operating-system-dependent.
Example
unixerror(5) == Input/output error /* under Windows XP, */
unixerror(7) == Arg list too long /* for example */
UPPER
UPPER(string)
Returns string translated to uppercase. Duplicates the function of the standard function
translate(string).
590
Regina Extended Functions
Example
say upper(‘abc’) == ‘ABC’
say upper(‘AbCd’) == ‘ABCD’
USERID
USERID()
Returns the name of the current user (his or her userid). If the platform cannot provide it, this function
returns the null string.
Example
/* here’s a Windows example, running under the Administrator */
userid() == Administrator
WRITECH
WRITECH(file, string)
Writes the string to the logical filename file and returns the number of bytes written.
Example
writech(‘outfile’,’hi’) /* writes string ‘hi’ to the file and returns 2 */
WRITELN
WRITELN(file)
Writes the string to the logical file specified by file with the end of line character(s) or newline
appended. Returns the number of bytes written, including the newline.
Example
writeln(‘outfile’,’hi2’) /* writes string ‘hi2’ to the file and returns 4
(since this value includes the newline) */
591
Mainframe Extended
Functions
This appendix provides a reference to the extended functions of VM/CMS and OS TSO/E Rexx. It
excludes the dozen or so Double-Byte Character Set (DBCS) functions. This appendix as intended
as a quick reference guide for developers, so please see the IBM mainframe Rexx manuals for full
details: VM REXX Reference, or TSO/E REXX Reference. You can download them from
www.RexxInfo.org.
Each entry is identified by the name of the function. Entries contain a template of the function,
showing its arguments, if any. Optional arguments are enclosed in brackets ([ ]). The template is
followed by a description of the function and its use, the function’s arguments, and possible
return codes.
Coding examples show how to code each function. We have noted where the functions differ under
VM/CMS versus TSO/E Rexx.
EXTERNALS
EXTERNALS()
For VM, returns the number of lines (or elements) in the terminal input buffer. This is the number
of logical typed-ahead lines.
Example
externals() == 0 /* if no lines are present */
For OS TSO/E, this function has no meaning since there is no terminal input buffer. Under OS
TSO/E, this function always returns 0.
Example
externals () 0 /* Under OS/TSO, 0 is ALWAYS returned */
Appendix E
FIND
FIND(string, phrase)
Returns the word number of the first occurrence of phrase in string. Returns 0 if phrase is not found.
Multiple blanks between words are treated as one in comparisons. The ANSI-1996 standard function
wordpos performs the same work and should be used instead when possible.
Example
find(‘now is the time’,’the’) == 3
find(‘now is the time’,’xxx’) == 0
find(‘now is the time’,’the time’) == 3
INDEX
INDEX(haystack, needle [,start])
Returns the character position of needle within string haystack. Returns 0 if needle is not found. If
specified, start tells where in haystack to initiate the search. It defaults to 1 if not specified. The ANSI-
1996 standard pos function is preferred over index.
Example
index(‘this’,’x’) == 0 /* not found */
index(‘this’,’hi’) == 2
index(‘thisthisthis’,’hi’) == 2 /* returns position of first occurrence */
JUSTIFY
JUSTIFY(string, length [,pad])
Evenly justifies words within a string. The length specifies the length of the returned string, while
pad specifies what padding to insert (if necessary). pad defaults to blank. The ANSI-1996 standard
right and left functions can be used as alternatives to justify.
Example
justify(‘this is it’,18) == ‘this is it’ /* 5 blanks between words */
justify(‘this is it’,18,’x’) == ‘thisxxxxxisxxxxxit’ /* 5 x’s between words */
justify(‘ this is it’,18,’x’) == ‘thisxxxxxisxxxxxit’ /* 5 x’s between words */
/* ignores leading/trailing blanks */
justify(‘this is it’,3) == ‘thi’ /* truncation occurs, LENGTH too short */
594
Mainframe Extended Functions
LINESIZE
LINESIZE()
Under VM, returns the current terminal line width (the point at which the language processor breaks
lines displayed by the say instruction). It returns 0 in these cases:
Example
linesize() /* returns the current terminal width */
linesize() == 0 /* one of the three conditions listed above pertains */
Unnor OS TSO/E, if tmo ncript rVnn in forogroVnn, rotVrnn tmo cVrront torminal lino wintm minVn 1 (tmo
point at wmicm tmo langVago proconnor broakn linon ninplayon by tmo say inntrVction). If tmo ncript rVnn in
backgroVnn, tmin fVnction alwayn rotVrnn 131. In non-TSO/E annronn npacon, tmin fVnction rotVrnn tmo
logical rocorn longtm of tmo outdd filo (nofaVlt in systsprt).
Example
linesize() /* if the script is running in foreground, returns terminal width */
linesize() == 131 /* script is running in background */
USERID
USERID()
Example
userid() == ZHBF01 /* if the login id were ZHBF01 */
Unnor OS, if tmo ncript in rVnning in a non-TSO/E annronn npaco, tmin fVnction rotVrnn oitmor tmo userid
npocifion, tmo stepname, or tmo jobname.
595
Rexx/SQL Functions
This appendix provides a reference to all Rexx/SQL functions, as defined by the product docu
mentation for Rexx/SQL version 2.4. This appendix is intended as a quick reference guide for
developers, so see the product documentation if more detailed information is required. Chapter 15
tells where to obtain this documentation.
Each of the following entries is identified by the name of the function. Entries contain a template
of the function, showing its arguments, if any. Optional arguments are enclosed in brackets ([ ]).
The template is followed by a description of the function and its use and the function’s arguments.
Coding examples show how to code each function. All Rexx/SQL functions return 0 upon success
unless otherwise noted.
SQLCLOSE
SQLCLOSE( statement_name )
Example
if SQLClose(sl) <> 0 then call sqlerr ‘During close’
SQLCOMMAND
SQLCOMMAND( statement_name, sql_statement [,bind1[,bind2[,...[,bindN]]]] )
statement_name — Names the SQL statement. For selects, names a stem for an array that will receive
statement results
Variable sqlca.rowcount is set by this function to the number of rows affected by DML statements.
Example
For a select statement:
Variables s1.deptno.0 and s1.dept_desc.0 will both be set to the number of rows returned.
Variables s1.deptno.1 and s1.dept_desc.1 will contain values retrieved for these respective columns
for the first row, variables s1.deptno.2 and s1.dept_desc.2 will contain values retrieved for these
respective columns from the second row, and so on.
Example
For a DDL statement:
SQLCOMMIT
SQLCOMMIT()
Example
if SQLCommit() <> 0 then call sqlerr ‘On commit’
SQLCONNECT
SQLCONNECT( [connection_name], [username], [password], [database], [host] )
Creates a new connection to the database. This becomes the default database connection. Which parame
ters are required, and what their values are, is database-dependent.
598
Rexx/SQL Functions
connection_name—Names the connection. Required if more than one connection will be open
simultaneously.
Example
Oracle—All parameters are optional:
rc = sqlconnect(‘MYCON’,,,’SAMPLE’)
SQLDEFAULT
SQLDEFAULT( [connection_name] )
connection name—The name of the database connection to set the default to. Optional.
SQLDESCRIBE
SQLDESCRIBE( statement_name [,stem_name] )
Run after a sqlprepare statement, describes the expressions returned by a select statement. Creates
a compound variable for each column in the select list of the SQL statement. The stem consists of
statement_name, followed by the constant “column” and one of the following attributes:
599
Appendix F
□ size — Column’s size
stem_name—Optional stem name for any variables created to return the information
Note that the columns returned by this function can be determined by calling the sqlgetinfo function
with the describecolumns argument.
Example
rc = sqlprepare(s1,”select deptno, dept_desc from dept_table”)
rc = sqldescribe(s1,”fd”)
fd.column.name.1 == ‘DEPTNO’
fd.column.name.2 == ‘DEPT_DESC’
fd.column.type.1 == ‘NUMBER’
fd.column.type.2 == ‘VARCHAR2’
fd.column.size.1 == ‘6’
fd.column.size.2 == ‘30’
fd.column.precision.1 == ‘0’
fd.column.precision.2 == ‘’
fd.column.scale.1 == ‘6’
fd.column.scale.2 == ‘’
fd.column.nullable.1 == ‘0’
fd.column.nullable.2 == ‘1’
SQLDISCONNECT
SQLDISCONNECT( [connection_name] )
Closes a database connection and all open cursors for that connection.
Example
if SQLDisconnect() <> 0 then call sqlerr ‘During disconnect’
600
Rexx/SQL Functions
SQLDISPOSE
SQLDISPOSE( statement_name )
Deallocates a work area originally allocated by a sqlprepare statement. Implicitly closes any associated
cursor.
Example
if SQLDispose(sl) <> 0 then call sqlerr ‘During dispose’
SQLDROPFUNCS
SQLDROPFUNCS([‘UNLOAD’])
Terminates use of Rexx/SQL and frees resources. The inverse of sqlloadfuncs. If the unload string is
coded as the only argument, all Rexx/SQL functions are removed from memory. Use this function with
caution as this can affect other running Rexx/SQL programs or other running threads in the current
program.
Example
if SQLDropFuncs(‘UNLOAD’) <> 0 then
say ‘sqldropfuncs failed, rc: ‘ rc
SQLEXECUTE
SQLEXECUTE( statement_name [,bind1[,bind2[,...[,bindN]]]] )
Executes a previously “prepared” insert, update, or delete statement. The sequence of Rexx/SQL
functions to execute these SQL statements in discrete steps would therefore be: sqlprepare, sqlexecute,
and sqldispose.
Variable sqlca.rowcount is set to the number of rows affected by the DML statement.
601
Appendix F
SQLFETCH
SQLFETCH( statement_name, [number_rows] )
Fetches the next row for an open cursor. If number_rows is specified, it can return more than one row.
For one-row fetches, a compound variable is created for each column name identified in the SQL state
ment. The stem is the statement name, and the tail is the column name.
For multiple-row fetches, a Rexx array is created for each column name in the SQL statement.
number_rows —An optional parameter that specifies how many rows to fetch
Returns 0 when there are no more rows to fetch. Otherwise, returns a positive integer.
Example
Here is a complete example of preparing a cursor, opening the cursor, fetching all rows from the cursor
select, and closing the cursor:
SQLGETDATA
SQLGETDATA(statement_name, column_name, [start_byte], [number_bytes] [,file_name] )
Extracts column data from column_name in the fetched row. The data is returned in a Rexx compound
variable, unless file_name is specified, in which case it is written to a file.
start_byte—Optionally specifies the starting byte from which to return column data
602
Rexx/SQL Functions
file_name—If specified, names the file into which complete column contents are written
Returns 0 when there is no more data to retrieve. Otherwise, returns the number of bytes retrieved.
You can usually code sqlfetch and then reference columns by their compound or stem variable names
without explicitly invoking sqlgetdata. See the entry under “sqlfetch” earlier in this appendix for
a complete example. Please see the Rexx/SQL documentation to see an example of the sqlgetdata
function.
SQLGETINFO
SQLGETINFO( [connection_name], variable_name [,stem_name] )
Returns information about the database referred to by the connection identified by connection_name.
connection_name—The database connection name. If not present, uses the current connection.
stem_name—If provided, the information is returned in variables that use this name as their stem. If the
information is not provided, this returns a data string.
Example
Get the database version information:
if SQLGetinfo(,’DBMSVERSION’,’desc.’) <> 0
then call sqlerr ‘Error getting db version’
else say ‘The database Version is: ‘ desc.1
SQLLOADFUNCS
SQLLOADFUNCS()
Loads all external Rexx/SQL functions. Call this function after loading it with rxfuncadd.
603
Appendix F
Example
Here is a typical code sequence to load and execute the sqlloadfuncs function to access the entire
Rexx/SQL function library. Be sure that the operating system can locate the external functions by setting
the proper environmental variable first:
SQLOPEN
SQLOPEN( statement_name [,bind1[,bind2[,...[,bindN]]]] )
Opens and instantiates a cursor for select processing. The statement must have previously been pre
pared by a sqlprepare statement.
Example
See the entry under “sqlfetch”for a complete select cursor-processing example.
SQLPREPARE
SQLPREPARE( statement_name, sql_statement )
Allocates a work area for a SQL DDL or DML statement and prepares it for processing. A select state
ment will subsequently be executed by cursor processing (sqlopen, sqlfetch, and sqlclose). All
other statements are executed by a subsequent sqlexecute.
Example
See the entry under “sqlfetch” for a complete select cursor-processing example.
604
Rexx/SQL Functions
SQLROLLBACK
SQLROLLBACK()
Discards and does not apply (rolls back) any pending database updates.
Example
if SQLRollback() <> 0 then call sqlerr ‘On rollback’
SQLVARIABLE
SQLVARIABLE( variable_name [,variable_value] )
Either returns or sets the specified variable. If variable_value is not present, returns the value of
variable_name. If variable_value is present, sets variable_name to that value. Can be used to set
various aspects of database behavior.
□ Package name
□ Rexx/SQL version
□ Rexx/SQL date
□ OS platform
□ Database
□ debug — debugging level:
□ savesql — If 1, the variable sqlca.sqltext contains the text of last SQL statement. Defaults to 1.
605
Appendix F
□ AUTOCOMMIT — Sets database auto-commit ON or OFF (1 is ON, 0 is OFF). Results depend on
how the database auto-commit feature works.
□ ignoretruncate—Dictates what action occurs when a column value gets truncated. Default
(OFF) results in function failure and error message. ON truncates data with no error.
□ nullstringout — Returns a user-specified string instead of the null string for a NULL column
value.
□ nullstringin — Enables a user-specified string to represent null columns. Defaults to the null
string.
□ supportsplacemarkers (a read-only value) — Returns 1 if the database supports bind place
holders, 0 otherwise.
□ standardplacemarkers —if 1, enables use of the question mark (?) as the placeholder for
databases that support placeholders other than the question mark (?).
□ supportsdmlrowcount (a read-only value) — Returns 1 if the database returns the number of
rows affected by insert, update, and delete statements, 0 otherwise.
Example
if SQLVariable(‘AUTOCOMMIT’) = 1
then say ‘Autocommit is ON’
else say ‘Autocommit is OFF’
if SQLVariable(‘SUPPORTSPLACEMARKERS’) = 1
then say ‘Database supports bind placeholders’
else say ‘Database does not support bind placeholders’
if SQLVariable(‘SUPPORTSDMLROWCOUNT’) = 1
then say ‘Database supports SQL Row Counts’
else say ‘Database does not support SQL Row Counts’
606
Rexx/Tk Functions
The following table lists the standard Rexx/Tk functions. The table is reproduced from the
Rexx/Tk documentation at http://rexxtk.sourceforge.net/functions.html . For further
details, please see the Rexx/Tk home page at: http://rexxtk.sourceforge.net/index.html .
608
Rexx/Tk Functions
609
Appendix G
610
Rexx/Tk Functions
Rexx/Tk Extensions
Rexx/Tk is supplied with a number of extension packages. These extensions enable access to additional
Tk widgets written in Tcl and are dynamically loaded by Tcl programmers as required. These widgets
are included in the base Rexx/Tk package. They are listed in the following tables.
The tables that follow are reproduced from the Rexx/Tk documentation at http://rexxtk.
sourceforge.net/extensions.html. For further details, please see the Rexx/Tk home page at
http://rexxtk.sourceforge.net/index.html .
611
Appendix G
TkTreeGetSelection(pathName) Tree:getselection
TkTreeGetLabel(pathName, x, y) Tree:labelat
TkTreeNodeConfig(pathName, name [,options...]) Tree:nodeconfig
TkTreeOpen(pathName, name) Tree:open
TkTreeSetSelection(pathName, label) Tree:setselection
Items marked with * are base Rexx/Tk functions that can be used with Rexx/Tk extensions.
612
Rexx/Tk Functions
613
Tools, Interfaces,
and Packages
As a universal scripting language with worldwide use, Rexx has spawned a large collection of
open source and free tools. There are literally too many Rexx tools, utilities, extensions, and
interfaces to track them all. This partial list shows some of the available tools and hints at their
breadth.
All tools listed here are either open source or free software. Most reside either at SourceForge at
www.SourceForge.net or GitHub at www.GitHub.com. Access www.RexxInfo.org for a
complete, up-to-date list of tools with links to download them all for free.
CUR for Rexx A version of “ObjectCUR for Object REXX” for clas
sic Rexx.
FileRexx Function Library Functions with new file I/O commands specific to
Windows.
FileUt Scripting interface for standard I/O.
GTK+ Modal dialog manager for Object REXX.
Hack Hexadecimal editor for Windows with Rexx script
capabilities.
HtmlGadgets Generates code snippets to support HTML coding.
HtmlStrings Generates HTML code.
HtmlToolBar Generates code snippets for HTML gadgets.
Internet/REXX HHNS Workbench A Common Gateway Interface external function
library from Henri Henault & Sons, France.
MacroEd Manages RexxEd macros.
MIDI I/O Function Library Enables input/output to MIDI ports.
MIDI Rexx Function Library Read, write, play and record MIDI files.
Mod_Rexx Applies Rexx to Apache Web page development.
Controls all Apache features through Rexx scripts.
ObjectCUR for Object REXX A cross-platform class library that includes func
tions for system information, logging, file system
control, FTP, Win32 calls, and text file support.
ODBC Drivers Open Database Connectivity (ODBC) drivers for
Microsoft Access databases, dBASE files, or Excel
files.
Regular Expressions Regular expressions for Rexx scripts.
REXREF3 Produces Rexx script cross-reference listings.
Rexx 2 Exe Converts scripts into a self-running *.exe files.
Rexx Dialog Creates GUI interfaces for Windows.
Rexx Math Bumper Pack Math libraries.
Rexx/CURL Interface to the cURL package for access to
URL-addressable resources.
Rexx/Curses Interface to the curses library for portable character
based user interfaces.
Rexx/DW Provides a cross-platform GUI toolset, based on the
Dynamic Windows package.
616
Tools, Interfaces, and Packages
617
Appendix H
618
Open Object Rexx Classes
These tables list the classes of Open Object Rexx. They are from the section The Builtin Classes in the
manual Open Object Rexx Reference.
Fundamental
Classes
The Object class is the root of the class hierarchy. The instance methods of the Object class are,
Object Class
therefore, available on all objects.
The Class class is like a factory that produces the factories that produce objects. It is a subclass of
Class Class the Object class (). The instance methods of the Class class are also the class methods of all
classes.
String objects represent character-string data values. A character string value can have any
String Class
length and contain any characters.
The Method class creates method objects from Rexx source code. It is a subclass of the Object
Method Class
class ().
The Routine class creates routine objects from Rexx source code. It is a subclass of the Object
Routine Class
class ().
The Package class contains the source code for a package of Rexx code. A package instance holds
all of the routines, classes, and methods created from a source code unit and also manages
Package Class
external dependencies referenced by ::REQUIRES directives. The files loaded by ::REQUIRES are
also contained in Package class instances. It is a subclass of the Object class ().
A message object provides for the deferred or asynchronous sending of a message. You can
Message Class create a message object by using the new method of the Message class or the start () method of
the Object class ().
Appendix I
Stream Classes
Collection Classes
The Collection class is a mixin class that defines the basic set of methods implemented by all
Collection Class Collections. Many of the Collection class methods are abstract and must be implemented the
inheriting subclasses.
The MapCollection class is a mixin class that defines the basic set of methods implemented by
MapCollection Class
all collections that use create a mapping from an index object to a value.
The OrderedCollection class is a mixin class that defines the basic set of methods implemented
OrderedCollection Class
by all collections that have an inherent index ordering, such as an array or a list.
This is a tagging mixin class only and does not define any methods of its own. Collections that
SetCollection Class implement SetCollection are MapCollections that constrain the index and item to be be the
same object.
An array is a possibly sparse collection with indexes that are positive whole numbers. You can
Array Class reference array items by using one or more indexes. The number of indexes is the same as the
number of dimensions of the array. This number is called the dimensionality of the array.
A bag is a non-sparse collection that restricts the elements to having an item that is the same as
Bag Class the index. Any object can be placed in a bag, and the same object can be placed in a bag several
times.
The CircularQueue class allows for storing objects in a circular queue of a predefined size. Once
the end of the queue has been reached, new item objects are inserted from the beginning,
CircularQueue Class
replacing earlier entries. Any object can be placed in the queue and the same object can occupy
more than one position in the queue.
A Directory is a MapCollection using unique character string indexes. The items of the
Directory Class
collection can be any valid Rexx object.
A list is a non-sparse sequenced collection similar to the Array class () to which you can add
new items at any position in the sequence. The List creates a new index value whenever an item
List Class is added to the list and the associated index value remains valid for that item regardless of
other additions or removals. Only indexes the list object generates are valid i.e. the list is never
a sparse list and the list object will not modify indexes for items in the list.
620
Open Object Rexx Classes
A properties object is a collection with unique indexes that are character strings representing names
Properties
and items that are also restricted to character string values. Properties objects are useful for
Class
processing bundles of application option values.
A queue is a non-sparse sequenced collection with whole-number indexes. The indexes specify the
Queue Class position of an item relative to the head (first item) of the queue. Adding/removing an item changes
the association of an index to its queue item. You can add items at either the tail or head of the queue.
A relation is a collection with indexes that can be any object. In a relation, each item is associated
Relation
with a single index, but there can be more than one item with the same index (unlike a table, which
Class
can contain only one item for any index).
A Set is a collection containing member items where the index is the same as the item (similar to a
Set Class Bag collection). Any object can be placed in a set. There can be only one occurrence of any object in a
set (unlike a Bag collection). Item equality is determined by using the == method.
Stem Class A stem object is a collection with unique indexes that are character strings.
A table is a collection with indexes that can be any object. In a table, each item is associated with a
Table Class single index, and there can be only one item for each index (unlike a relation, which can contain more
than one item with the same index). Index equality is determined by using the == method.
An identity table is a collection with indexes that can be any object. In an identity table, each item is
Identity associated with a single index, and there can be only one item for each index. Index and item matches
Table Class in an identity table are made using an object identity comparison. That is, an index will only match if
the same instance is used in the collection.
Utility Classes
622
Mod_Rexx: Functions and
Special Variables
This appendix lists the Mod_Rexx functions and special variables. Mod_Rexx is the package that
permits Rexx scripts to control all aspects of the popular open source Apache Web server product.
Chapter 17 describes Mod_Rexx and demonstrates how to script it. See that chapter for a full
product description and sample program.
General Functions
These functions provide a base level of services necessary to work with the Apache Web server.
They manage cookies and the error log, retrieve environmental information, and handle URLs.
624
Mod Rexx: Functions and Special Variables
625
Appendix J
Special Variables
Mod_Rexx uses a set of three dozen special variablesto communicate information to Rexx scripts. The names
of these variables all begin with the letters www. These special variables are set either before the script starts,
or after the script executes a function call. Their purpose is to communicate information to the script either
about the environment or the results of function calls. Here are the Mod_Rexx special variables:
626
Mod Rexx: Functions and Special Variables
627
■<
n
NetRexx: Quick Reference
This appendix provides a quick summary of NetRexx. For full authoritative reference, see the book
The NetRexx Language by Michael Cowlishaw (Prentice-Hall, 1997). Also refer to the manuals that
download with the product: NetRexx Language Reference; NetRexx Programming Guide; The NetRexx
Language: Specification; and the NetRexx Language Supplement.
ask Reads a line from the default input stream and returns it as a
string of type Rexx (also called a NetRexx string)
digits Returns the current setting of numeric digits as a NetRexx
string
form Returns the current setting of numeric form as a NetRexx
string
length Returns an array’s length (the number of elements)
null Returns the null value (used in assignments and comparisons)
source Returns a NetRexx string that identifies the source of the
current class
super Used to invoke a method or property overridden in the current
class
this Returns a reference to the current object
trace Returns the current setting as a NetRexx string
version Returns the Ne tRexx language version as a NetRexx string
Appendix K
Special Methods
Special Method Use
Instruction Syntax
This section lists the NetRexx instructions. Each consists of a coding template with allowable operands.
Optionally coded operands are surrounded by brackets ([ ]). Operands in italicized boldface are to be
replaced by an appropriate term or list. This is intended as a quick programmer’s reference. For greater
detail, please see the NetRexx documentation cited in the introduction to this appendix.
CLASS
class name[ visibility ] [ modifier ] [ binary ]
[ extends classname ]
[ uses classname_list ]
[ implements classname_list ] ;
DO
do [ label name ] [ protect term] ;
instruction_list
[ catch [vare= ] exception; instruction_list] . . .
[ finally [;] instruction_list ]
end [ name ] ;
EXIT
exit [ expression ]
IF
if expression[;]
then [;] instruction
[ else [;] instruction]
630
NetRexx: Quick Reference
IMPORT
import name
ITERATE
iterate [ name] ;
LEAVE
leave [ name ]
LOOP
loop [ label name ] [ protect termp ] [ repetitor ] [ conditional ]
instruction_list
[ catch [vare= ] exception; instruction_list] . . .
[ finally [;] instruction_list ]
end [ name ] ;
varoover termo
for expression_r
forever
METHOD
method name[( [ argument_list] )]
[ visibility ] [ modifier ] [ protect ]
[ returns termr]
[ signals signal_list ] ;
NOP
nop ;
NUMERIC
numeric digits [ expression_d ] ;
or
OPTIONS
options options_list
PACKAGE
package name
PARSE
parse term template
PROPERTIES
properties [ visibility ] ] odifierr ]
RETURN
return [ expression]
632
NetRexx: Quick Reference
SAY
say [ expression]
SELECT
select [ label name] [ protect termp] ;
when expression[;] then [;] instruction . . .
[ otherwise [;] instruction_list ]
[ catch [vare= ] exception; instruction_list ] . . .
[ finally [;] instruction_list ]
end [ name ] ;
SIGNAL
signal term;
TRACE
trace trace_term;
633
Retrieving System
Information
Table L-1 parse source system Strings
This table lists some example system information strings returned by the instructions:
Since this information is system-dependent and subject to change, readers should retrieve this
information for their own environments. You can use the sample script on the next page.
Here's a script you can run called rexx_info.rexx that executes the above statements and displays their outputs:
636
Answers to “Test Your
Understanding”
Questions
Chapter 1
1. Rexx is a higher-level language in that each line of code accomplishes more than does code
written in traditional languages like C++, Java, or Pascal. Rexx derives its power from the
fact it is a glue language — a language that ties together existing components such as other
programs, routines, filters, objects, and the like. The industry-wide trend towards
scripting languages is based on the higher productivity these languages yield.
2. Rexx is a free-format language. There are no requirements to code in particular columns or
lines or in uppercase, lowercase, or mixed case.
3. Expert programmers sometimes mistakenly think that they don’t need an easy-to-use
language. Nothing could be farther from the truth. Expert programmers become wildly
productive with easy-to-use languages. Their code lasts longer as well, because less
skilled individuals can easily enhance and maintain it.
4. The two free object-oriented Rexx interpreters are roo! from Kilowatt Software and Open
Object Rexx from the Rexx Language Association (formerly known as IBM’s Object
REXX). Both run standard or classic Rexx scripts without any alterations.
5. One outstanding feature of Rexx is that it runs on all sizes of computer, from cell phones
and handhelds running Android, to personal computers, to midrange machines and
departmental servers, to mainframes.
6. One of the two current Rexx standards was established by the book The Rexx Language,
second edition, by Michael Cowlishaw, published in 1990. The other was promulgated
by the American National Standards Institute, or ANSI, in 1996. There is little
difference between these two standards. Chapter 13 lists the exact differences between
these two similar standards.
Appendix M
7. Rexx bridges the traditional gap between ease of use and power through: simple syntax; free
formatting; consistent, reliable behavior; a small instruction set surrounded by a large set
of functions; few language rules; support for modularity and structured programming; and
standardization.
Chapter 2
1. Comments are encoded between the starting identifier /* and the ending identifier */. They
may span as many lines as you like. They may also appear as trailing comments, comments writ
ten on the same lines as Rexx code.
2. Rexx recognizes functions as keywords immediately followed by a left parenthesis: function
_name() or function_name(parameter). The call instruction can also be used to invoke
functions. In this case, the function is encoded just like a call to a subroutine, and parentheses
do not immediately follow the function name. Chapter 8 fully discusses how to invoke
functions and subroutines.
3. Variables do not have to be predefined or declared in Rexx. They are automatically defined
the first time they are used or referred to. If a variable is equal to its name in uppercase, it is
uninitialized.
4. The basic instruction for screen output is say. The basic instruction for keyboard input is pull.
Rexx also offers more sophisticated ways to perform input and output, described in subsequent
chapters.
5. Comparisons determine if two values are equal (such as character strings or numbers). Strict
comparisons only apply to character strings. They determine if two strings are identical
(including any preceding and/or trailing blanks). The strings are not altered in any way prior to
a strict comparison. For example, the shorter string is not blank-padded as in regular or
“nonstrict” character string comparison.
6. Define a numeric variable in the same way you define any other Rexx variable. The only
difference is that a numeric variable contains a value recognized as a number (such as a string
of digits, optionally preceded by a plus or minus sign and optionally containing a decimal
place, or in exponential notation).
Chapter 3
1. Structured programming is recommended because it leads to more understandable code, and
therefore higher productivity. The first table in the chapter lists the structured programming
constructs and the Rexx instructions that implement them. Subroutines and functions support
modularity, the key structured programming concept of breaking code up into discrete,
smaller routines.
2. Rexx matches an unmatched else with the nearest unmatched if.
3. Test for the end of input by testing for the user’s entry of a null string or by inspecting input for
some special character string denoting the end of file in the input (such as end or exit or x).
Chapter 5 introduces functions that can test for the end of an input file, such as chars and
lines.
638
Answers to “Test Your Understanding” Questions
4. Built-in functions are provided as part of the Rexx language. The code of internal routinesresides
in the same file as that of the calling routine; external routines reside in separate files. A Rexx
function always returns a single value; a subroutine may optionally return a value. Chapter 8
gives full details on how to pass information into and out of functions and subroutines.
5. true tests to 1. false tests to 0. Standard Rexx does not accept “any nonzero value” for
TRUE. (However, some specific Rexx interpreters will accept any nonzero value as TRUE).
6. The danger of a do forever loop is that it will be an endless loop and never terminate. Avoid
coding the do forever loop; use structured programming’s do-while loop instead. If you
do code a do forever loop, be sure to code a manual exit of some sort. For example, the
leave, signal, and exit instructions can end the otherwise endless loop.
7. The signal instruction either causes an unconditional branch of control, or aids in processing
special errors or conditions. signal differs from the GOTO of other languages in that it
terminates all active control structures in which it is encoded.
8. do while tests the condition at the top of the loop, while do until is a bottom-driven
loop (it tests the condition at the bottom of the loop). Only the do while is structured. Its
use is preferred. Any do until can be recoded as do while.
Chapter 4
1. Any number of subscripts can be applied to array elements. An array may have any desired
dimensionality. The only limit is typically that imposed by memory. Array elements do not
have to been referenced by numbers; they may be referenced by arbitrary character strings also.
This is known as an associative array.
2. All elements in an array can be initialized to some value by a single assignment statement, but
other operations cannot be applied to an entire array. For example, it is not possible to add
some value to all numeric elements in an array in a single statement. Use a simple loop to
accomplish this. A few Rexx interpreters do allow additional or extended array operations. The
chapters on specific interpreters in Section II of this book cover this.
3. Rexx does not automatically keep track of the number of elements in an array. To process all
elements in an array, keep track of the number of elements in the array. Then process all array
elements by a loop using the number of array elements as the loop control variable.
Alternatively, initialize the entire array to some unused value (such as the null string or 0), prior
to filling it with data elements. Then process the array elements using a loop until you
encounter the default value. These two array processing techniques assume you use a numeric
array subscript, and that contiguous array positions are all used. To process all elements in an
array subscripted by character strings, one technique is to store the index strings in a list, then
process that list against the array, one item at a time.
4. Arrays form the basis of many data structures including lists, key-value pairs, and balanced and
unbalanced trees. Create a list with a one-dimensional array (an array in which elements are ref
erenced by a single subscript). Create key-value pairs by matching subscripts with their
corresponding values. Create tree structures by implementing an element hierarchy through the
array.
639
Appendix M
Chapter 5
1. The two types of input/output are line-oriented and character-oriented. Use the former to read and
write lines of information, and the latter to read and write individual characters. Line-oriented
I/O is typically more portable across operating systems. Character-oriented is useful in reading
all bytes in the input stream, regardless of any special meaning they might have to the operating
system.
2. The stream function is used either to return information about a character stream or file, or
to perform some action upon it. The stream function definition allows Rexx interpreters to
offer many implementation-dependentfile commands, and most do. Look the function up in
your product documentation to learn what it offers for I/O and file manipulation. The statuses
it returns are ERROR, NOTREADY, READY, and UNKNOWN.
3. Encode the I/O functions immediately followed by parentheses — for example, feedback
= linein(filein)— or through a call instruction (for example, call linein
filein). Capture the return code as shown in the example for the first method, or through
the result special variable for the call method. It is important to check the return code for
I/O operations because this informs your program about the result of that I/O and whether or
not it succeeded.
4. Rexx does not require explicitly closing a file after using it. Program end automatically closes
any open files. This is convenient for short scripts, but for longer or more complex scripts, explicitly
closing files is a good programming practice. This prevents running out of memory (because each
open file uses memory) and may also be necessary if a program needs to “reprocess” a file. The
typical way to close a file is either to encode a lineout or charout function that writes no
data, or to encode the stream function with a command parameter that closes the file.
5. Rexx offers several options beyond standard I/O for sophisticated I/O needs. One option is
to use a database package, such as one of those described in Chapter 15. Another option is to
use Rexx interpreter I/O extensions (discussed in Chapters 20 through 30 on the specific Rexx
interpreters).
Chapter 6
1. String processing is the ability to process text. It is critically important because so many
programming problems require this capability. Examples include report writing and the
building and issuing of operating system commands.
2. Concatenation can be performed implicitly, by encoding variables with a single space between
them; by abuttal, which means coding variables together without an intervening blank; or
explicitly, by using the string concatenation operator: ||.
3. The three methods of template parsing are: by words, in which case each word is identified; by
pattern, which scans for a specified character or pattern; and by numeric pattern, which processes
by column position.
4. The functions to use are: verify, datatype, pos, delstr, right and left, and strip. Given
the flexibility of the string functions, you might choose other string functions and combine them
in various ways to achieve these same operations.
5. wordindex returns the character position of the nth word in a string, while wordpos returns
the word position of the first word of a phrase within a string.
640
Answers to “Test Your Understanding” Questions
6. Hex characters each represent strings of four bits; character strings are composed of consecutive
individual characters of variable length, where each character is internally made up of 8 bits; bit
strings consist solely of 0s and 1s. Rexx includes a full set of conversion functions, including: b2x,
c2d, c2x, d2c, d2x, x2b, x2c, and x2d.
7. Bit strings can be used for a wide variety of tasks. Examples mentioned in the chapter include
bit map indexes, character folding, and key folding.
Chapter 7
1. numeric digits determines how many significant digits are in a number. This affects
accuracy in computation and output display. numeric fuzz determines the number of
significant digits used in comparisons. You might set fuzz in order to affect just a single
comparison, while keeping the numeric precision of a number unchanged.
2. Scientific notation has one digit to the left of the decimal place, followed by fractional and
exponential components. Engineering notation expresses the integer component by a number
between 1 and 999. Rexx uses scientific notation by default. Change this by the numeric form
instruction.
3. There are several ways to right-justify a number; one of them is the format function.
4. The datatype function allows you to check many data conditions, including whether a value
is alphanumeric, a bit string, all lower- or uppercase, mixed case, a valid number or symbol, a
whole number, or a hexadecimal number.
-22 valid
' -22 ' valid- the blanks are ignored
2.2. invalid- has a trailing period
2.2.2 invalid- more than one decimal point
222b2 invalid- contains an internal character
2.34e+13 valid
123.E -2 invalid- no blanks allowed in exponential portion
123.2 E + 7 invalid- no blanks allowed in exponential portion
Chapter 8
1. Modularity is important because it underliesstructured programming. Modularity reduces errors
and enhances program maintenance. Rexx supports modularity through its full set of structured
control constructs, plus internal and external subroutines and functions.
2. A function always returns a single string through the return instruction. A subroutine may or
may not return a value. Functions return their single value such that it is placed right into the
statement where the function is coded, effectively replacing the function call. Get the value
returned by a subroutine through the result special variable. Functions can be coded as
embedded within a statement or invoked through the call instruction. Subroutines can only
be invoked via the call instruction.
3. Internal subroutines reside in the same file as the main routine or driver. External subroutines
reside in separate files. procedure can be used to selectively protect or expose variables for
an internal routine. External routines always have an implicit procedure so that all the
caller’s variables are hidden.
641
Appendix M
4. The function search orderdetermines where Rexx searches for called functions. It is: internal
function, built-in function, external function. If you code a function with the same name as a Rexx
built-in function, Rexx uses your function. Override this behavior by coding the function name
as uppercase within quotes.
5. Information can be passed from a caller to a routine by several methods, including: passing
arguments as input parameters, procedure expose, and using global variables. Updated
variables can be passed back to the calling routine by the return instruction, changing expose'd
variables, and changing global variables.
6. A procedure instruction without an expose keyword hides all the caller’s variables.
procedure expose allows updating the variables, whereas those read in through arg are
read- only.
7. In standard Rexx condition testing, expressions must resolve to either 1 or 0, otherwise an error
occurs. Some Rexx interpreters are extended to accept any nonzero value as TRUE.
Chapter 9
1. The default setting for the trace facility is trace n (or Normal). trace r is recommended for
general-purpose debugging. It traces clauses before they execute and the final results of
expression evaluation. It also shows when values change by pull, arg, and parse instructions.
trace l lists all labels program execution passes through and shows which internal routines
are entered and run. trace i shows intermediate results.
2. The trace facility is the basic tool you would use to figure out any problems that occur while
issuing operating system commands from within a script. The trace flags C, E, and F would
be useful for tracing OS commands.
3. To start interactive tracing, code the trace instruction with a question mark (?) preceding
its argument. The ? is a toggle switch. If tracing is off, it turns it on; if tracing is on, it turns it
off. The first trace instruction or function you execute with ? encoded turns tracing on.
The next one that executes with the question mark will turn it off.
4. When in interactive mode, the Rexx interpreter pauses after each statement or clause. Use it to
single-step through code.
Chapter 10
1. The purpose of error or exception trapping is to manage certain kinds of commonly occurring
errors in a systematic manner. The conditions are ERROR, FAILURE, HALT, NOVALUE,
NOTREADY, SYNTAX and LOSTDIGITS. The ANSI-1996 standard added LOSTDIGITS.
2. To handle a control interrupt, enable an error trap for the halt condition. Enable this
exception condition, then Rexx automatically invokes your error routine when this condition
occurs.
3. signal applies to all seven error conditions. call does not apply to syntax, novalue, and
LOSTDIGITS errors. signal forces an abnormal change in theflow of control. It terminates
any do, if or select instruction in force and unconditionally transfers control to a specified
label.
642
Answers to “Test Your Understanding” Questions
call provides for normal invocation of an internal subroutine to handle an error condition.
The result special variable is not set when returning from a called condition trap; any value
coded on the return instruction is ignored.
5. Enable a condition routine through a signal or call instruction. You can have multiple
routines to handle a condition by coding signal or call multiple times, but only one
condition routine is active for each kind of error trap at any one time.
6. If the error trap was initiated by the signal instruction, after executing the condition trap
routine, you must reactivate the error condition by executing the signal instruction again.
7. Whether it is better to write one generic error routine to handle all kinds or errors, or to write a
separate routine for each different kind of error, depends on what you’re trying to do and the
nature of your program. Both approaches have advantages. Sometimes it is convenient to
consolidate all error handling into a single routine, other times, it may be preferable to have
detailed, separate routines for each condition.
Chapter 11
1. All Rexx implementations covered in this book have a stack; however, how the stack is
implemented varies. Review the product documentation if you need to know how the stack is
supported within your version of Rexx.
2. Stacks are last-in, first-out structures, while queues are first-in, first-out. Instructions like push
and queue place data into the stack, and instructions like pull and parse pull extract
data from it. The queued built-in function reports how many items are in the stack.
3. The limit on the number of items the stack can hold is usually a function of available memory.
4. The answer to this question depends on the Rexx interpreter(s) you use and the platforms to
which you wish to port. Check the documentation for the platforms and interpreters for which
you intend to port.
5. Some Rexx interpreters support more than one stack, and more than one memory area or buffer
within each stack. Functions or commands like newstack and delstack manage stacks,
while makebuf, dropbuf, and desbuf manage buffers. Check the documentation for your
specific Rexx interpreter regarding these features, as they do vary by interpreter.
Chapter 12
1. Consistency in coding is a virtue because it renders code easier to understand, enhance, and
maintain.
2. Some programmers deeply nest functions because this makes for more compact code. Some
developers find it an intellectually interesting way to code, and others even use it to
demonstrate their cleverness. If overdone, it makes code indecipherable and result in slower
execution of the program.
643
Appendix M
3. A good comment imparts information beyond what the code shows. It explains the code
further, in clear English. Rexx comments may appear on the end of a line, in stand-alone lines,
or in comment boxes.
4. Modularity and structured programming permit a limited number of entry and exit points from
discrete blocks of code. This makes code easier to understand and follow, and restricts
interactions between different parts of programs. The result is higher productivity, and more
easily understood code that is easier to maintain.
5. do until and signal are unstructured control instructions. Any code using them can be
rewritten as structured code by use of the do while and if statements.
6. A good variable name is descriptive. It is not short or cryptic but long and self-explanatory. It
does not employ cryptic abbreviations but instead fully spells out words. Good variable naming
makes a program much more readable.
7. Global variables can be highly convenient when coding. But best programming practice limits
their use in larger and more complex programs. Structured programming involves carefully
defined interfaces between routines, functions, and modules. Variables should be localized to
routines and their use across routines should be carefully defined and limited. Global variables
do not follow these principles. Different programmers and sites may have their own standards
or opinions on this matter.
Chapter 13
1. No. Writing portable code typically takes more effort than writing nonportable code. In some
cases, where the goal is quick coding, a nonportable solution may meet the goal more
effectively.
2. Scripts can learn about their environment in several ways. Key instructions for this purpose
include parse version and parse source. The chapter also lists many other instructions
and functions that help scripts learn about their environment.
3. arg automatically translates input to uppercase, while parse arg does not.
4. The sourceline function either returns the number of lines in the source script, or a
specific line if a line number is supplied as an argument
5. The appendix of the TRL-2 book lists all its differences from TRL-1.
Chapter 14
1. Rexx sends a command string to the environment when it does not recognize it as valid Rexx
code. The default environment, the environment to which external commands are directed by
default, is typically the operating system’s shell or command interface.
2. Enclose commands in quotation marks when their contents will otherwise be incorrectly
interpreted or evaluated by the Rexx interpreter. For example: dir > output.txt will not
work because the > symbol will be interpreted as a “greater than” symbol by Rexx, rather than
as a valid part of the OS command. So, this OS command would fail unless enclosed within
quotation marks. Some developers like to enclose all of the OS command string in quotes,
except the parts they specifically want Rexx to evaluate. Others quote only those parts of the
644
Answers to “Test Your Understanding” Questions
command that must not be evaluated. We generally follow the latter approach in this book.
Either technique works fine; it is a matter of preference as to which you use.
To prepare a command in advance, assign the command string to a Rexx variable prior to
issuing it to the operating system. You can easily inspect the variable’s contents merely by
displaying it.
3. Basic ways to get error information from OS commands include inspecting their command
return codes, capturing their textual error output, and intercepting raised condition traps within
error routines. Look up return code information for OS commands in the operating system
documentation.
4. Two ways to redirect command input/output from within a script are the address instruction
or through the operating system’s redirection symbols. Command I/O redirection works on
operating systems in the Windows, Linux, Unix, BSD, and DOS families, among others. Not all
operating systems support I/O redirection.
5. Sources and targets can be specified as arrays or streams (files). They may be intermixed within
the same address command.
6. To direct all subsequent external commands to the same interface, specify address with a
system target only (without any external command encoded on the same instruction).
Repeated coding of address without any environment operand effectively “toggles” the
target for commands back and forth between two target environments.
Chapter 15
1. Rexx/SQL is free, open source, universal, and standardized. Database programming provides
sophisticated I/O for multiuser environments. Advantages to database management systems
include backup/recovery, database utilities, central data administration, transaction control,
and many other features.
2. Scripts typically start by loading the Rexx/SQL function library for use through the rxfuncadd
and SQLLoadFuncs functions.
3. Connect to a database by sqlconnect, and disconnect through the sqldisconnect function.
Check connection status by SQLGetInfo. You can also check the return code for any Rexx/SQL
function to verify its success or failure.
4. Consolidating error handling in a single routine is typical in database programming. It allows
consistent error handling, while minimizing code. Here are the SQLCA variables set by
Rexx/SQL:
□ sqlca.sqlcode — SQL return co de
645
Appendix M
5. Assigning a SQL statement to a Rexx variable makes its coding clearer. It can also be verified
simplifying by displaying the variable’s value via a simple say instruction.
6. sqldisconnect terminates a connection with a database and closes any open database
cursor(s) for that connection. sqldispose releases the work area (memory) resources originally
allocated by a sqlprepare function.
7. Rexx/SQL is a database-neutral product that is free, open source, very capable, and widely
used. It offers both generic and native database interfaces to nearly any available database.
Database-specific Rexx interfaces are also available from a few relational database companies.
These products are proprietary and support only that company’s database. In exchange, they
often offer access to database-unique features, for example, the ability to write Rexx scripts for
database administration or for controlling database utilities. An example of a proprietary inter
face is IBM Corporation’s DB2 UDB interface described in this chapter.
Chapter 16
1. Both Rexx/Tk and Rexx/DW are free, open source interfaces that allow you to create portable
graphical user interfaces for Rexx scripts. Rexx/Tk is based on the widely used Tk toolkit and
provides Rexx programmers entree into the Tcl/Tk universe. Rexx/DW is a lightweight protocol;
it does not have the overhead that Rexx/Tk does. Both products offer very large libraries of GUI
functions and features.
2. Rexx Dialog was designed specifically for scripting Windows GUIs. It works with both the
Reginald and Regina Rexx interpreters.
3. A widget is a control or object placed on a window with which users interact. Widgets are
added in Rexx/Tk by functions like tkadd and tkconfig and others. Widgets are
packed onto DW window layouts.
4. The basic logic of GUI scripts is the same, regardless of whether Rexx/Tk or Rexx/DW is used.
Register and load the function library; create the controls or widgets for the topmost window;
display the top-level window; wait for user interaction with the widgets, handle the interactions
requested through event-handling routines; and terminate the window and the program when
requested by the user.
5. Tcl/Tk GUI toolkit has achieved worldwide use because it renders inherently complex windows
programming relatively simple. It is also portable and runs on almost any platform.
6. Rexx/gd creates graphical images, not GUIs. These images can be used as part of a GUI (for
example, as components placed on a Web page). Rexx/gd creates its images in memory work
areas. The images are typically stored on disk after developed, then the script releases the image
memory area and terminates.
Chapter 17
1. Yes, you could write Web programs without using any of the packages described in the
chapter. However, you would be doing a lot more work, and essentially duplicating code and
routines that already exist and that you can freely use.
646
Answers to “Test Your Understanding” Questions
2. Functions htmltop and htmlbot write standard headers and footers, respectively. Scripts
write the Content Type header by the PrintHeader function. The content type header must be
the first statement written to the browser. It tells the browser the kind of data it will receive in
subsequent statements. Read user input through the ReadForm function, among others.
3. Function cgiinit initializes and sets up the CGI header, while cgiend typically ends a script.
CgiHref generates a hyperlink.
4. Mod_Rexx makes the Apache open source Web server completely programmable by Rexx
scripts. Apache scales better than traditional CGI Web server programming because Apache
handles incoming connections much more efficiently. Mod_Rexx gives the same capabilities to
Rexx scripts as Perl programs get from mod_perl and PHP scripts from mod_php.
5. Rexx Server Pages are analogous to Java Server Pages or embedded PHP scripting, in that RSPs
permit embedding Rexx code directly into the HTML of Web pages. This permits “dynamic
pages” that are tailored or customized in real time.
6. Short- and long-form delimiters identify and surround Rexx code within HTML pages. They are
used with Rexx Server Pages, or RSPs. There is no functional difference between short- and
long-form delimiters.
7. To customize Apache’s log processing, use the Mod_Rexx package. It enables you to code Rexx
scripts that control any of the 14 or so processing steps of the Apache Web server, including
the one that manages log processing.
Chapter 18
1. XML is a self-describing data language. XML files contain both data and tags that describe the
data. They are textual files. XML is useful for data interchange between applications or compa
nies. XPath is a standard for identifying and extracting parts of XML files. XSLT applies defini
tional templates called stylesheets to XML files. It can be used to transform XMLfiles. HTML is
a language that defines Web pages.
2. Function xmlparsexml can be used to load and optionally validate a document. Function
xmlSaveDoc saves a document, while function xmlFreeDoc frees resources.
3. Function xmlparsehtml can parse or scan a Web page written in HTML. xmlfindnode and
xmlNodesetCount may also be useful, as per the example in the chapter.
4. Rexx does not include regular expressions. However, many packages are freely available
that add this facility to the language, including RexxRE and Regular Expressions. Appendix H
lists many of the free and open source packages, tools, and interfaces for Rexx programmers.
5. Apply an XSLT stylesheet to a document by the xmlApplyStylesheet function.
xmlParseXSLT parses and compiles an XSLT stylesheet, while xmlFreeStylesheet frees
a compiled stylesheet. xmlOutputMethod reports the output method of a stylesheet.
Chapter 19
1. BRexx runs fast on limited resource computers. Regina and BRexx run under virtually any
imaginable platform. Rexx interpreters that are extended specifically for Windows include
Regina, r4, and Reginald. Several interpreters offer extensions for Unix, Linux, and BSD,
especially Rexx/imc, BRexx, and Regina.
647
Appendix M
2. The major Rexx standards are TRL-1, TRL-2, ANSI-1996, and SAA. Most Rexx interpreters
adhere to TRL-2, while Regina is the primary offering that implements full ANSI-1996. SAA
was IBM’s attempt to rationalize its diverse operating systems in the 1990s. SAA declared Rexx
its common procedures language. The practical effect was that IBM ported Rexx to all its
operating systems and established more rigorous standardization for the language across
platforms.
3. Open Object Rexx and roo! are fully object-oriented Rexx interpreters. Both are supersets of
standard or classic Rexx. This means that you can take a standard Rexx script and run it under
either Open Object Rexx or roo! without any changes to that script. roo! is free under Windows,
while Open Object Rexx is free under Windows, Linux, Unix, BSD, macOS, and Android.
4. NetRexx runs under the Java Virtual Machine (JVM). So, it runs anywhere Java runs. It
presents an easy-to-use alternative to Java and may be freely intermixed with Java scripts. So,
for example, NetRexx can make full use of the Java class libraries. You can write applications,
classes, Java Beans, and servlets in NetRexx. NetRexx is a Rexx-like language; it does not meet
the Rexx standards such as TRL-2 or ANSI-1996.
5. Regina is the open source Rexx that it includes many of the extended functions offered in other
Rexx interpreters.
6. BRexx, ooRexx for Android, and Rexx for Android (or Rexxoid) all run on Android cell
phones. Chapter 25 covers them in detail.
7. Emulation is slower and less efficient than running in native mode. However, emulation has the
immediate benefit that it ports applications without code changes.
Chapter 20
1. Regina is open source, widely popular, well supported, and meets all standards. It runs on
virtually every platform, including any version of Windows, Linux, Unix, macOS, and BSD. It
also runs natively under a very wide variety of lesser-used operating systems.
2. Use the stack to manage command I/O and pass information between routines. Regina also has
a unique stack facility that permits communications between different processes on the same
machine, and even between different processes on different machines. Regina also supports
sending and receiving I/O to/from commands using the address instruction with standard
keywords like input, output, and error.
3. Use readch and writech to read and write character strings, and readln and writeln to
read and write lines. Use open to explicitly open a file, close to explicitly close a file, eof to
test for end of file, and seek to move the file pointers.
648
Answers to “Test Your Understanding” Questions
4. The SAA API is an interface definition that allows programs written in languages like C to use
Regina as a set of services or a function library. Regina uses offers SAA-compatible functions
for loading external function libraries. These include rxfundadd to register an external
function and rxfundrop to remove external functions from use.
5. Regina supports a wide variety of parameters on the options instruction, including cms,
unix, buffers, arexx_bifs, regina, ansi, saa, trl2, and trl1. See the Regina
documentation for full details on these and other options instruction parameters.
Chapter 21
1. Rexx/imc runs under all forms of Linux, Unix, and BSD. Rexx/imc meets the TRL-2 standards.
Its advantages include its strong Unix heritage and orientation, extra Unix functions, good doc
umentation, and a strong track record of support.
2. Rexx/imc includes C-like I/O functions such as open, close, stream, and ftell. This I/O
model provides more explicit control than Rexx’s standard I/O. Use the standard I/O functions
for portability and standardization, and use the the C-like I/O functions for more explicit
file control.
3. select can key off the values in a variable in Rexx/imc, rather than requiring condition tests.
This is useful in implementing a CASE construct based on the value of a variable.
4. Use rxfuncadd to load and register and external function for use, and rxfuncdrop to drop
an external function. Use rxfuncquery to determine if a function is already loaded.
5. Rexx/imc accepts any nonzero value as TRUE, whereas standard Rexx only accepts 1 as TRUE.
In this respect, any standard Rexx script will run under Rexx/imc, but a script written for
Rexx/imc’s approach to TRUE conditions might fail when run under standard Rexx.
Chapter 22
1. BRexx’s advantages include its high performance, small footprint, wide array of built-in func
tions, and extra function libraries. It runs on a wide variety of platforms and in addition is
uniquely positioned among Rexx interpreters to run on smaller, limited-resource environments.
It also runs on mainframes or in mainframe emulation in its download called BRexx/370.
2. Position a file pointer through the seek function. Using seek, you can position to anywhere in
the file including its beginning or end. The seek function can also return current file pointer
positions. Use it to determine the size of a file by this statement: filesize =
seek(file_pointer, 0, “eof”).
3. The EBCDIC functions convert data between ASCII and EBCDIC (these are the two predominant
coding schemes, with the former being used on PCs and midrange machines, and the latter
being used for mainframes). The Date Functions external function library can handle date
arithmetic.
4. The stack buffer functions include makebuf (create a new system stack), desbuf (destroy
all system stacks), and dropbuf (destroy the top n stacks).
649
Appendix M
5. BRexx supports standard Rexx I/O, C-like I/O, and database I/O through MySQL. Standard
Rexx I/O is for general use in standards-based, portable programs; C-like I/O is nonstandard
but offers more explicitfile control; and the MySQL functions provide the full power of a rela
tional database. The latest releases also support SQLite, a database alternative to MySQL.
6. Code operating system commands in the same manner as with any standard Rexx interpreter.
You can also encode them as if they were functions, if they use standard I/O. Capture command
output through the stack, or code a command as if it were a function and capture its return
string through an assignment statement.
Chapter 23
1. Reginald’s biggest advantage is that it is specifically tailored and customized for Windows.
Furthermore, it offers many add-on tools, provides great documentation on how to use its
extended features and functions, permits use of any Windows DLL, meets the Rexx TRL-2 stan
dards, and supports the SAA API.
2. Reginald comes with complete documentation that includes examples of every new function
and feature. You shouldn’t require any information other than what comes with Reginald to
use it.
3. Use Reginald’s SAA-compliant functions like rxfuncadd, rxfuncquery, and rxfuncdrop to
access external function libraries. Use funcdef to access DLLs that were not written to Rexx’s
specifications. You can autoload many function libraries through Reginald’s Administration
Tool and thereby avoid explicitly loading them in every script. This is very convenient and
also reduces the amount of code you must write.
4. Reginald accesses virtually any external data source, including office products like Microsoft
Excel and Access, and databases like SQL Server, MySQL, and PostgreSQL. The Open
Database Connectivity or ODBC drivers are the means to accomplish this.
5. Reginald offers a freely downloadable tutorial on how to use Reginald with the Common
Gateway Interface, or CGI. Other tutorials address subjects like mailslots, Internet access, sock
ets, and GUI programming.
6. driveinfo and matchname are two functions (among others) that supply information
about disk drives. MatchName also provides attribute information. A file does not have to be
open to retrieve information about it, but it must exist.
7. Valuein reads binary values in as numeric values.
8. The loadtext function reads lines of a text file into a stem variable, or saves a stem
variable’s lines to a text file.
9. The RxDlgIDE add-on product helps generate GUI code.
10. RxErr establishes how GUI-related errors will be handled; RxCreate creates a new
window with its controls; RxMsg controls user interaction with a window. The key values
scripts check to determine how a user interacted with a window and its controls are rxid
and rxsubid.
11. The Speech library allows the computer to synthesize speech. The MIDI function library con
trols the MIDI interface, which connects a computer to external instrumentation. Use MIDI to
send information to a musical instrument, and the Speech library to read a document aloud.
650
Answers to “Test Your Understanding” Questions
Chapter 24
1. SBCs typically lack human interface devices like displays, keyboards, touchpads, mice, etc.
They also usually have lesser processor and memory resources, which directly affects how
you go about programming them.
2. SBCs are often sold without cases because they’re often embedded in other systems. They don’t
need to be “dressed up” like consumer laptops and desktops.
3. NetRexx requires Java support because it runs on the Java Virtual Machine. ooRexx only
requires Java support if you use it with the BSF4ooRexx Java integration tool. Some of the JDKs
available include those from Oracle Corporation, BellSoft, and the OpenJDK.
4. Apache Subversion is a software versioning and revision control system. Use it to manage
software and ensure you install the proper versions.
5. BSF4ooRexx provides Java integration for ooRexx. It is not needed or used with NetRexx.
6. No, you don’t need to learn object-oriented programming, because ooRexx is a superset of
classic Rexx, and you can code strictly procedurally with it if you so choose.
7. You need a pin-interface software tool, like Broadcom or WritingPi. Pi4J can help manage this.
Chapter 25
1. Scripting Layer for Android(or SL4A) is a scripting host. It allows developers to automate
Android tasks in scripting languages instead of Java. Rexx is one of several scripting languages
it supports.
2. BRexx is the only one of the three interpreters in this chapter that requires and uses SL4A.
3. The Manifest file manages permissions. Unless you have some other obvious problem, this is
what you need to edit to fix a permissions problem.
4. At the present time, you must root your device to install and run ooRexx for Android. That is
anticipated to change soon.
5. You direct commands to Android by address system.
6. The bluetoothConnect function knows which device to contact by the identifier coded as
its operand.
651
Appendix M
Chapter 26
1. Among the advantages to r4/roo! are that they are specifically tailored for Windows, they fit
together as a classic Rexx/object Rexx pair, and they share a large set of Windows-specific tools.
Among the tools are AuroraWare! (to create GUIs), TopHat (for creating fill-in-the-blank forms),
Poof! (with 135 command-line aids and tools), Revu (to view text), XMLGenie! (to convert XML
to HTML), Chill (to hide Rexx source code), and the Exe Conversion Utility (to convert scripts to
stand-alone executable files).
2. r4 scripts run under roo! without any alteration. roo! scripts will typically not run under r4,
because roo! is a superset of classic Rexx and r4. Since r4 scripts are classic Rexx, they are widely
portable across operating systems. roo! scripts are portable only across varieties of Windows,
because roo! is a Windows-specific product.
3. Several of the r4/roo! utilities aid in building GUIs, AuroraWare! being the most directly perti
nent. In the list of attributes, the r4/roo! GUI tools offer the best customization for Windows, are
easiest to use, can be learned most quickly, and are easiest to maintain. Competing tools such as
Rexx/Tk and Rexx/DW are the most portable and the most powerful. These statements are gen
eralizations that may not apply across the board to all projects, so always assess the available
tools versus the criteria and goals of your own project.
4. Installation of r4 and roo! is simple and similar to other Windows installs. The one variant is
that there is a preinstall step that Web-installs on the user’s system.
5. roo! supports all the principles of object-oriented programming, including classes and methods,
inheritance, an hierarchical class structure, encapsulation, abstraction, and polymorphism. As in
all object-oriented systems, messages invoke methods in the various classes.
6. roo! supports a number of new operators in order to bring full object-orientation to classic Rexx.
These include the following:
7. For line-oriented I/O, use classes such as inlinefile and outlinefile. To manage the
display screen, the Console class is useful. instream and outstream handle the default
input and output streams. Use the Socket class to manage TCP/IP sockets.
652
Answers to “Test Your Understanding” Questions
Chapter 27
1. Open Object Rexx is a superset of classic Rexx. Classic Rexx programs typically run under Open
Object Rexx without any alteration. ooRexx features inheritance, the ability to create subclasses
that inherit the methods and attributes of their superclasses. Multiple inheritance allows a class to
inherit from more than one superclass. Open Object Rexx’s class hierarchy implements all this.
2. Encapsulation means that only an object can manipulate its own data. Send a message to the
object to invoke its methods to manipulate that data. Polymorphism means that messages
invoke actions appropriate to the object to which they are sent.
3. The Stream class has I/O methods. It goes beyond classic Rexx capabilities to include reading/
writing entire arrays, binary, and text I/O, shared-mode files, direct I/O, and so on.
4. Open Object Rexx adds new special variables. Two are used for referencing objects: Self
references the object of the current executing method, while Super references the superclass
or parent of the current object.
5. Actually, there are more than four directives. The most frequently used ones are: ::CLASS (to
define a class), ::METHOD (to define a method), ::ROUTINE (to define a callable subroutine),
and ::REQUIRES (to specify access to another source script). Directives mark points within the
script at which these definitions or actions apply.
6. Collection classes manipulate sets of objects and define data structures. A few of the collection
classes are: Array (a sequenced collection), Bag (a nonunique collection of objects), Directory (a
collection indexed by unique character strings), List (a sequenced collection which allows
inserts at any position), and Queue (a sequenced collection that allows inserts at the start or
ending positions).
7. The new USER condition trap allows user-defined error conditions. Explicitly raise an error
condition (user-defined or built-in) by the new raise instruction.
8. As in classic Rexx, the expose instruction permits access to and updating of specified
variables. In object programming, expose permits access to objects as well as string variables.
Chapter 28
1. Every classic Rexx program will run under Open Object Rexx, but this does not take advantage
of any of the new object-oriented capabilities of Open Object Rexx. Compatibility both preserves
legacy scripts and allows you to tip-toe into object-oriented scripting at a pace at which you are
comfortable.
2. All instructions and functions of classic Rexx are part of Open Object Rexx. The latter adds a
number of new and enhanced instructions and functions.
3. Collections are classes that implement data structures and allow manipulation of the objects in
the class as a group.
653
Appendix M
5. The stream class offers a superset of the I/O capabilities of classic Rexx. Extra features it
includes are: reading/writing entire arrays, binary and text I/O, shared-mode files, direct I/O,
and so on.
6. The four most used directives are: ::CLASS (to define a class), ::METHOD (to define a method),
::ROUTINE (to define a callable subroutine), and ::REQUIRES (to specify access to another
source script). Classes and methods are placed after the main routine or driver (towards the end
of the script).
Operator Use
[] Same as the at method
8. These monitor objects are applied against the default streams. The default streams are .stdin,
.stdout, and .stderr.
Chapter 29
1. Mainframe Rexx generally meets the TRL-2 and SAA standards. There are slight differences
between Rexx interpreters on the different mainframe platforms as well as between mainframe
Rexx and the standards. Appendix R enumerates the exact differences between IBM
mainframe REXX and the ANSI 1996 Rexx standard.
2. Mainframe Rexx enhances the instructions options and parse and adds the extended main
frame instruction upper. Mainframe extended functions include externals, find, index,
justify, linesize, and userid. In addition, both VM and OS Rexx add their own external
functions, but what is included in this list varies between VM and OS.
3. Mainframe Rexx supports the Double-Byte Character Set, which allows encoding for
ideographic languages, such as Asian languages like Chinese and Korean.
5. Immediate commands can be entered from the command line and they affect the executing
script in real time. Immediate commands can turn the trace on or off, suspend or resume script
execution, and suspend or resume terminal (display) output.
6. VM Rexx knows about and automatically loads any or all of these three named packages if any
function within them is invoked from within a script: RXUSERFN, RXLOCFN, and RXSYSFN. Users
may add their own functions to these libraries.
7. Rexx compilers separate the compile (or script preparation step) from its execution. This usually
produces an executable that runs faster, purchased at the price of a two-step process. If a Rexx
script is stable and unlikely to change, and is run frequently, compiling it might increase its
run-time performance. Note that the mainframe compiler does not guarantee a performance
increase.
8. VM Rexx requires a comment line, while OS TSO/E Rexx requires the word rexx. To satisfy
both requirements, encode this as the first line in a mainframe Rexx script: /* REXX */.
654
Answers to “Test Your Understanding” Questions
9. VM Rexx scripts reside in files of type exec. XEDIT editor macros reside in files of type xedit.
CMS pipelines use files of type REXX.
Chapter 30
1. NetRexx offers some of the traditional advantages of Rexx: easy syntax, ease of use, and ease of
maintenance. You can intermix NetRexx and Java classes however you like.
2. The NetRexx translator can be used as a compiler or an interpreter. As an interpreter, it allows
NetRexx programs to run without needing a compiler or generating .class files. As a
compiler, it can compile scripts into .class files. NetRexx programs can be both compiled and
interpreted in just one command. This is easy to do and machine-efficient. NetRexx can also
generate formatted Java code, including original commentary, if desired.
3. NetRexx scripts run on any machine that offers a Java Virtual Machine (JVM). This makes them
portable across all Java environments. As an interpreter, the NetRexx translator allows
NetRexx programs to run without needing a compiler or generating .class files.
4. Indexed strings are NetRexx strings by which subvalues are identified by an index string.
Arrays are tables of fixed size that must be defined before use. Arrays may index elements of
any type, and the elements are considered ordered. To define a dictionary collection class,
refer to the Java documentation. NetRexx uses all the Java classes and methods.
5. NetRexx uses all the Java classes and methods, so refer to the Java documentation for information.
6. *.nrx files contain the source of a NetRexx script. *.java files contain Java source code,
and can be produced automatically from NetRexx scripts by the NetRexx translator. *.class
files contain the binary or compiled form of a program.
7. Special names perform an action wherever they appear in the source. For example, ask reads a
line from the default input stream and returns it as a string of type Rexx while length returns
an array’s length (the number of elements). The special methods include super, the constructor
of the superclass, and this, the constructor of the current class.
8. To migrate classic Rexx scripts to NetRexx, download the free Rexx2Nrx classic Rexx to
NetRexx automated conversion tool.
655
N
How to Use EXECIO
by Howard Fosdick and Lionel B. Dyck
Overview
Chapter 5 covered how to perform input and output operations in Rexx scripts. The I/O model is
often called streaming, because Rexx considers both input sources and output targets as sequences of
characters or bytes.
One of the benefits of the streaming model is that it applies regardless of whether you're working
with disk files, printers, or displays. It readily lends itself to I/O redirection -- the ability to alter a
program's input/output stream without altering the program code itself.
Rexx requires fewer than a dozen instructions and functions to effectively manage I/O streams.
They include parse, chars, charin, charout, lines, linein, lineout, and stream.
These support:
□ Character-oriented I/O -- a stream of individual bytes
□ Line-oriented I/O -- reading and writing lines (or records)
□ Conversational I/O -- interaction with users at their displays
□ Redirected I/O -- switching I/O sources and targets without altering Rexx programs
IBM Rexx for the VM operating system natively supports streaming. OS systems require
installation of the Stream I/O for TSO/E Rexx Function Package for streaming support.
A big advantage to I/O streaming is that it conforms to the Rexx standards. It thus provides
portability of both program code and programmer skills across platforms (except for OS systems
lacking the Streaming Function Package).
An alternative I/O facility that has long been available on mainframes is the EXECIO command.
EXECIO stands for "Execute I/O".
EXECIO is a highly popular alternative to streaming I/O on mainframes. If you work there, you'll
either use it or run across it. EXECIO is available on all mainframes -- on the operating systems we
generically refer to as the OS, VM, and VSE families -- but no other platforms.
Appendix N
□ Allows processing of multiple records -- or even an entire dataset -- with a single command
Why EXECIO?
Use EXECIO to perform these tasks:
□ Read or write an entire file using a compound or stem variable -- an array -- in a single operation
EXECIO can only operate on sequential datasets, or members of partitioned datasets. It supports all the standard
record formats: fixed or variable length, blocked or unblocked.
Allocating Files
Since Chapter 29 on mainframe Rexx concentrated on VM program examples, this chapter will focus on TSO/E.
The mechanics and coding of the EXECIO command are very similar on these systems (but not exactly the same).
In most programs, before your script processes a file, you have to allocate that file for use. Use the TSO
ALLOCATE command for this. (You'll often see it abbreviated as ALLOC, FILE as F, and DATASET as DA.)
658
How to Use EXECIO
Example (1) allocates a new dataset with the same file parameters as the LIKE dataset it names. The new
dataset inherits its characteristics from the existing file.
Example (2) allocates a new file for use with the specified characteristics. These dataset definition parameters
directly correspond to the equivalents you would code in Job Control Language (JCL).
In all cases, the ddname name on the ALLOCATE statement corresponds to the DD statement you would code if
you were coding JCL. The dataset name is the same as you would code in your DSN= parameter in JCL.
Of course, when your Rexx program concludes, you need to de-allocate any files it uses. Use the FREE statement
for this. Here are example templates:
Example (2) conveniently frees all datasets allocated to your Rexx program in a single statement.
EXECIO Basics
Now let's explore the EXECIO statement.
Example (1) opens a dataset for processing, while example (2) closes the dataset after you're done processing it.
The 0 that occurs right after the keyword EXECIO tells how many lines (or records) you want to process.
When it is 0, that means you're processing zero records. You're either just opening or closing the file.
OPEN means you're opening the file for use. FINIS means you're closing the file after your use of it is
complete. These keywords appear in parentheses after the other operands.
Note that it is acceptable (and very common) not to code the matching right parenthesis. The code shown above
with a single left parenthesis is perfectly fine.
DISKR indicates you're reading a file. The two alternatives are DISKW for writing to a file, and DISKRU to indicate
a read operation for update.
The ddname must match that given in a previous ALLOCATE statement. This is how TSO matches up your
EXECIO statement to the file it should operate upon. So that ddname would match the one in an ALLOCATE
statement, like this one, for example:
659
Appendix N
Reading Data
EXECIO reads data from a file into one of these places:
If you don't specify a variable name, EXECIO's default is to read data into the stack for subsequent processing
by your program. So you would normally follow your EXECIO read statement with Rexx code that then
processes the data in the stack.
When you do specify a variable on the EXECIO statement, you can supply the stem name of a compound
symbol. In other words, you use Rexx's notation to specify an array (a table) into which to read the file data.
Once you have issued the EXECIO and have read data into this Rexx array, you can process it however you like.
Let's look at a few examples of reading data:
Example (1) reads a single record into the data stack. Coding 1 tells EXECIO to read one record. The lack of a
variable name in the statement means that, by default, the data will be read into the stack.
As always, the ddname -- here indd -- links the EXECIO to the proper ALLOCATE ifstatement and tells it
which file to process.
Example (2) reads 100 records from the file into the stem variable mydata. In other words, this populates 100
consecutive slots in the array called mydata. with 100 records from the file.
The keyword STEM must be immediately followed by a stem variable into which to read the records. Note that
it is very important to include the period as the last character in the input variable name:
mydata. not mydata
This is how you indicate that your variable is the stem of a compound variable, rather than just a simple
variable and not an array. (Review Chapter 4 if you need a refresher on how Rexx uses compound variables and
their stems to represent arrays.)
Example (3) codes an asterisk instead of a specific number of lines to read. The result is that EXECIO reads all
the records from the file into the compound variable (or table) named myarray. So, an asterisk means to read
(or write) an entire dataset.
The FINIS keyword automatically closes the file following the read operation.
Whenever you do an "array read" into a compound variable, EXECIO will place the number of lines read into
the 0th array position. In these examples, that's into the variables named mydata.0 and myarray.0. This is
very useful information for your program's subsequent array processing.
660
How to Use EXECIO
Remember that in Rexx, if you enclose a statement within quotation marks, it will be sent to the
default environment for processing. (On OS systems, that default environment will be TSO/E.)
For example, in these statements, EXECIO reads a single line from a file into the data stack:
In some cases you may want to have the ddname dynamically substituted into the EXECIO statement
from a Rexx variable. This gives your code more flexibility.
In this case, be careful with your quotation marks.
The ddname will only be substituted in correctly if it exists outside of quotation marks. Here's an
example. You can see how ddname is a Rexx variable whose value will be substituted into the
EXECIO statement before it is passed to TSO for processing:
ddname = "my.input.ddname"
"EXECIO * DISKR" ddname "(FINIS STEM myarray."
More Examples
Let's round out this discussion with a few more example EXECIO statements:
Example (1) writes all the contents of the Rexx array to the output file. DISKW indicates to write
output. The asterisk indicates that all lines should be written, and STEM mydata. indicates the
source of the lines to write. FINIS will automatically close the file after processing.
EXECIO stops writing data when it encounters the first null variable in the array. So it processes
sequentially through the array -- you can't have null values interspersed inside your array data if you
intend to write it all out. You can fill in the 0th array element before writing the data, but it doesn't
matter: EXECIO doesn't use it.
Example (2) writes 20 lines to the output file from the mydata. array.
661
Appendix N
Example (3) shows how to position to a specific record in the sequential file for reading. The 9 combined with
keyword SKIP tells EXECIO to skip past the first 9 lines of the file. A subsequent read will start at record
number 10.
Example (4) issues a Rexx QUEUED function to discover how many lines are in the data stack. The subsequent
EXECIO refers to this number when writing the stack's contents to the output file.
Note the proper use of the quotation marks to ensure that variable substitution occurs for the Rexx variable
number_in_q.
FINIS closes the dataset after the write operation completes.
Stack Processing
After issuing an EXECIO statement that reads data into the stack, your Rexx program will likely process that
data in some manner.
Remember that element zero in the array (eg, element myarray.0 ) tells you how many lines EXECIO has
placed on the stack as a result of its read operation.
You might want to employ the various TSO/E commands that manipulate stacks: NEWSTACK, DELSTACK,
and QSTACK. Buffer commands can be useful too: MAKEBUF, DROPBUF, QBUF, and QELEM.
In Chapter 29, pages 502 and 503 explain these commands and provide examples of their use.
z/VM CMS provides very similar stack and buffer manipulation commands. These are summarized on page 499
in Chapter 29.
Example Programs
You can see that the processing loop uses the value that EXECIO put into array element 0 ( myarray.0 ) to
determine the number of lines that were read into the array. The DO loop then processes all the array elements.
Here's an example program that copies the contents of a data file to a new dataset. It's taken from the IBM
manual TSO/E REXX User's Guide.
662
How to Use EXECIO
This example shows how you can use stack manipulation commands like NEWSTACK and
DELSTACK to create and manage your own data stack. (It can be useful to create a new stack for your
program, otherwise you won't be able to distinguish what the stack already contained versus what
your program read into it.) Since the program uses a stack it created (rather than a Rexx array), it
needs to manually append a null entry to the stack with the QUEUE instruction. The EXECIO DISKW
statement depends on the presence of the null entry to know when to stop writing out data.
Here's a last example, written by mainframe expert and author Lionel B. Dyck. This program reads a
dataset, finds all lines that contain the string DSN= , and then generates a list of all dataset names
(DSNs) that appear in the input file.
The program starts by accepting a command line argument that tells it the name of the file to process.
It then feeds this argument into the ALLOC statement. Note how the double quotation marks are
coded so that variable substitution occurs for the Rexx variable named input.
The EXECIO statement then reads the entire dataset into the array or table named in.
The finis keyword closes the dataset after it's read, and the FREE command deallocates it.
The do loop processes all items in the array, driven the by number of items that EXECIO set in
array variable in.0.
The pos function determines if the string DSN= occurs in a line of data. If so, the dataset name is
isolated and added to the array named out.
After all lines have been processed, the final ALLOC statement assigns the output to the user's
terminal or display. This is the function of the asterisk in this code:
"Alloc f(out) ds(*)"
663
Appendix N
The final EXECIO writes the data to the user in a single statement. FREE deallocates the dataset.
One other point to note. This program allocates and deallocates datasets as needed (when they will be used).
This contrasts to the common coding practice of allocating all files a script will use at its start, then deallocating
all of them when the script ends.
This script's design is more efficient because it locks file resources for the minimal possible duration. In a quick
running program, the difference may not matter, but in a long-running program, this approach is more
Summary
In this appendix we explored the uses of the EXECIO statement and how to code it. EXECIO is very flexible and
powerful, and yet it is easy to code and understand.
EXECIO is quite popular in mainframe environments. Some sites don't use Rexx's streaming I/O facilities for file
processing at all.
EXECIO is common to all three mainframe operating systems, OS, VM, and VSE. The command is very similar
-- but not exactly the same -- across all three platforms. We've described its use here on OS TSO/E systems.
EXECIO is usually not available when running Rexx on systems other than mainframes.
You can find further information on EXECIO in several IBM manuals, especially the TSO/E REXX User's Guide
and the TSO/E REXX Reference for OS systems. For VM systems, look in the z/VM CMS Command and Utilities
Reference. All manuals are available for free download at www.RexxInfo.org.
The Stream I/O Function Package for TSO/E Rexx is described in detail in the manual IBM Compiler and Library
for REXX on IBM Z.
664
How to Write Edit Macros
by Howard Fosdick and Willy Jensen
Overview
Ever wish you could tailor the behavior of your text editor to your own preferences? Or make it
easier and quicker to use?
An edit macro is a Rexx script that you run that affects or drives the behavior of your text editor. It can make it easier
for you to:
□ Eueeueeeapedted tasks
□ AutomaOe eoutine er eepeViteaa tasks
□ Simplify your use oithe editor
□ Customize or ialior or extepd your editor
Thia appendix exploixa how tu write Rexx edit mocaua tu work with common editors. We’ll
exilain ehe citing aequiaefeneasia facaia ant walt ehaiugh a ciuile exafilea.
Whether you cox write ox edit macro fua oxp poaticuloa editor depends ox whether that editor
auppurta thia seature. Sume cummun text editura puu can write Rexx edit macrua sur include:
□ IS-a-availebxe uommtmainframes
□ Xe—a - amaioframe eotitor widulydrux onzM^M
□ EH a - avaHeSOr forWinclowrz Uinux, Unix, BSD, and mauy athetpiatforms
□ KEDIa - a summerriaiWmdowseditar
Appendix O
You typically create your Rexx macro as a member of a partitioned dataset. They can reside in any of several
concatenations, normally SYSPROC, SYSUPROC, SYSEXEC, SYSUEXEC, or an activated ALTLIB.
The name of the dataset you create is your macro’s name. Thefirst line of code should contain the word REXXinside a
comment, such as: /* REXX */
The second statement in the macro should direct commands to the edit processor, ADDRESS ISREDIT.
Remember that otherwise Rexx sends commands to the default command environment (TSO on z/OS systems).
You can look at an edit macro as a subcommand to the ISPF editor. It’s executed just as if it were an editor
subcommand. To run it, you just enter its name on the editor’s command line.
□ Edib comcommands
□ TSO commanda
□ Rh xx attthohnta
A Simple Example
Hhrh’a t aioplh hxtoplh. Thia otcro ntohd ONLY froo Willy Jhnahn diapltya only linha conttining aphcific conthnt.
Thh iahr inpita whtt thtt atring conthnt ia ta t coootnd linh ptrtohthr whhn hh rina thh otcro. Hhrh’a thh codh:
666
How to Write Edit Macros
As required, the macro starts with a first line comment containing the word rexx. The first
executable line of code is the Address isredit statement. It captures the user's input
parameter into the variable PRM. The NOPROCESS parameter simply means that the macro will be
executed after all the ISPF edit line commands are processed.
The second Address isredit statement has no command associated with it. So, that tells the
processor to direct all subsequent commands in the script to the editor (rather than the default of
TSO/E).
Editor commands reset and x all remove all messages and lines from view in the data
viewing area.
The command "find all" prm then completes the program's task. It ensures that all the
lines containing the string in the prm variable appear in the data area. So the macro exits.
Another Example
Here's another useful macro from Willy Jensen. This one is called UNIQUE. It deletes any
duplicate records from the dataset you're editing:
/* scan data */
"(lines) = linenum .zl"
dn=0
Do lnr=1 to lines
"(s) = Line (lnr)"
s='a'strip(s,'t')
if sf.s<>'' then do
"label" lnr "= .xdup"
"flip .xdup"
dn=dn+1
end
else sf.s=lnr
End
"delete all x"
XMSG:
parse arg zedlmsg
address ispexec"setmsg msg(isrz000)"
return 0
667
Appendix O
This macro starts with the same three lines as the previous example. These declare that it’s written in Rexx,
that it’s an edit macro, and that all subsequent lines Rexx passes to an outside environment for execution
will go to the editor rather than to TSO.
The program logic starts after the /* scan data */ comment. The number of lines we’re processing is assigned
to the variablelines. This value controls thedoloop, as the loop processes one line at a time.
Inside the loop, you see a clever use of an associative array to see if a line is a duplicate that has been processed
previously. This logic is directed at the compound variable (array) named sf.. (To learn about associative arrays, see
Chapter 4.)
If a line is determined to be a duplicate, this command assigns it a label as such: "label" lnr "= .xdup"
In this case, this statement increments the counter of duplicated lines: dn=dn+1
This command completes the processing by deleting all the lines marked as duplicated: "delete all x"
A Last Example
Sometimes it’s educational to review code written by different people to get an idea of stylistic differences. So here’s
an ISPF macro example from a different source.
This final example is straight from the IBM z/OS manual, ISPF Edit and Edit Macros. That’s the essential reference
manual for learning more about how to write edit macros. Along with background information, that manual lists and
defines all edit line commands, primary commands, macro commands, and assignment statements. (You can
download it along with all other Rexx-related IBM manuals at www.RexxInfo.org.)
This example program is namedISRMBRS. It enables you to make global changes to all members in a partitioned data
set. Or you could use it to search all PDS members for a specific data string.
The way it works is that you start this macro and pass to it the name of the or macro you want to run against all PDS
members. Then it turns around and applies that macro to each member of the PDS.
You invoke this macro from the editor command line as: ISRMBRS macname
macname is the name of the macro you want run against each PDS member.
668
How to Write Edit Macros
/*REXX****************************************************************/
/* ISPF edit macro to process all members of partitioned data set, */
/* running a second, user-specified, ISPF edit macro against each */
/* member. */
/* */
/* To run: */
/* Enter "ISRMBRS macname" on the command line, where macname is */
/* the macro you want run against each member. */
/*********************************************************************/
/*********************************************************************/
/* Free the member list and close the dataid for the PDS. */
/*********************************************************************/
Address ispexec 'LMMLIST DATAID('datal') OPTION(FREE)'
Address ispexec 'LMCLOSE DATAID('datal')'
Exit 0
669
Appendix O
To explain the program, it starts with the keyword REXX in its first comment line, and with the ISREDIT
MACRO statement in its first executable line.
Next, the first code block gathers dataset information and issues an LMOPEN to access it. The program uses
the ispexec statement to invoke ISPF services (as opposed to ISREDIT, which you use to identify edit
macros).
The next coding of Address ispexec invokes the LMMLIST ISPF service. This obtains each PDS
member for processing.
The do while loop then issues your macro command against each PDS member. Again, the program is
using ISPF services to perform this work. This is the Address ispexec 'EDIT command.
After all PDS members have been processed, the last two lines of code invoke ISPF services to free up the
member list and close the dataset.
Summary
This appendix introduced you to Rexx edit macros. You've seen that they can be quite useful for automating
all sorts of tasks within an editor like ISPF, Xedit, or THE (The Hessling Editor). If you use an editor day in
and day out, edit macros can save you lots of time in handling repetitive tasks. You might well decide to
tailor your editor to your personal preferences by using them.
Of course, this very brief introduction just scratched the merest surface of what you can do with edit macros.
To learn more, the website www.RexxInfo.org will direct you to many websites that offer freeware
macros. Or, just google for "ISPF Rexx macros" or "The Hessling Editor macros" and you'll find hundreds
of usable code examples.
If you code ISPF editor macros, you should access the IBM manual ISPF: Edit and Edit Macros. It includes
background information on how to create ISPF edit macros. It also lists all the edit line commands, edit
primary commands, and edit macro commands and assignment statements. These definitions let you
know what's available to code in your macros and how to code it.
The IBM manual ISPF Reference Summary also details the ISPF Editor commands that you can use in edit
macros.
670
Mainframes: How to Run
Rexx in Batch
Introduction
This article tells how to run Rexx as a mainframe batch job. It relies on examples drawn from the
IBM manuals.
It's not required in all cases, but it's recommended that you include REXX inside a comment in the first
line of your Rexx program.
This method is simple. But your Rexx program can not use TSO/E services, TSO commands, or
most TSO/E external functions. Your Rexx program can only perform those functions available to
programs running in a non-TSO/E address space.
Code your Rexx program as a member of a partitioned dataset (PDS). The dataset name is effectively
your program's name.
IRXJCL is a Rexx processor, so it will be the program name on the EXEC statement. You pass it
the name of your Rexx program (its PDS member name) by using the PARM keyword.
Appendix P
IRXJCL requires 3 DDNAMES:
1. SYSEXEC -- the PDS where your program resides (some sites may use SYSPROC instead)
2. SYSTSIN -- terminal input
3. SYSTSPRT -- where terminal output is sent to
Thus a minimal job skeleton to run a Rexx program called MYPROG looks like this.
As in all these examples, your JCL should reside in a fixed block, 80-byte record data set.
Here's a more sophisticated example. It shows how to pass an argument into the Rexx program, as well as
how to provide it input data in-stream. This example is straight from the IBM manual z/OS TSO/E REXX
User's Guide.
In this example, the program name is USERIDA, and it runs a Rexx exec called JCLTEST. JCLTEST is a
member residing in the partitioned dataset as USERID.MYREXX.EXEC(JCLTEST). The argument 'Test
IRXJCL' follows the member name and is passed to that REXX program as an input parameter. The Rexx
program can read in the argument through the statement:
672
How to Run Rexx in Batch
In-stream data input is through the SYSTSIN. Output goes to SYSTSPRT (the DSN named
USERID.IRXJCL.OUTPUT). If you wanted output to go to a printer instead of that dataset, you would
specify something like:
//SYSTSPRT DD SYSOUT=A
In this example, the batch job USERIDA runs the TSO/E processor IKJEFT01. As in the previous
example, the name of the Rexx program is MYPROG. It resides in a partitioned dataset specified by
SYSEXEC and named USERID.MYREXX.EXEC.
The Rexx program to run is specified in the SYSTSIN input stream. Once again, SYSTSPRT defines
the output location.
Instead of specifying your program name via SYSTSIN, you could code it as a PARM to the
IKJEFT01 program, as in the previous IRXJCL example.
Assume that you have coded this JCL and placed it in the partitioned dataset named REXX.JCL. That
data set should be defined as fixed blocked, 80-byte records. You could start it as a background job
by issuing the SUBMIT command, followed by the dataset name:
SUBMIT rexx.jcl
673
Appendix P
As an alternative to IKJEFT01, you could instead run either IKJEFT1A or IKJEFT1B. The minor differences
between the three programs mainly have to do with how they handle return codes and abends. For most
situations, IKJEFT01 works fine.
The above JCL coding example is straight from the IBM manual z/OS TSO/E REXX User's Guide. That manual
also details the minor differences between IKJEFT01, IKJEFT1A, and IKJEFT1B.
As this example shows, you start the TSO Terminal Monitoring program by running program IKJEFT01.
Invoking the ISPSTART command after the SYSTSIN input DDNAME is what starts up the ISPF environment.
Note how you specify your program name MYPROG as an ISPF command:
One big requirement here is that you allocate the ISPF libraries. These include ISPPLIB (for ISPF panels),
ISPMLIB (for ISPF messages), ISPTLIB (for ISPF tables), and ISPPROF (for ISPF profiles). You many have to ask
your system programmer the names of these libraries if they differ from those typical shown here.
ForISPTLIB(the ISPF tables), specify a temporary data set as itsfirst data set. If you don't, and two jobs run
concurrently, one of the jobs could fail with this error message:
674
How to Run Rexx in Batch
Similarly, specify a temporary data set for ISPPROF (the ISPF profile). Or, if you specify an existing data set,
be sure to specify a Disposition of Old (DISP=OLD). If you don't, you could get that ENQUEUE FAILED
message shown above.
You'll often see additional DDNAMES included. These may be required, depending on the kind of processing
involved. Examples are ISPSLIB for ISPF skeletons, ISPLLIB (for load modules), ISPTABL (for any output
tables), SYSPROC or SYSEXEC (for any CLISTs or REXX scripts), and ISPLOG (to show any logged
messages.)
First, remember that there is no user interaction in batch processing. So you have to handle the case where
user input is required for any of the DISPLAY services (DISPLAY, TBDISPL, SELECT PANEL, SETMSG,
and PQUERY).
One method is to change the DISPLAY service to use the COMMAND parameter:
stkbuf = 'END'
'DISPLAY PANEL('panname') COMMAND(stkbuf)'
Or, you can alter panels to simulate an END or ENTER key by using the .RESP keyword in your panel
definition:
)INIT
.RESP = ENTER
It's possible to encounter an infinite loop during display processing. To prevent this, modify the ISPSTART
command by adding the BDISPMAX parameter to limit the total number of displays allowed by your
program. BREDIMAX ensures the dialog ends after a specified number of redisplays:
You can also add ISPSTART parameters like BATSCRW (to define the screen width), and BATSCRD (to
define screen depth). This command starts ISPF with proper limits to display errors, and a screen width of
132 and depth of 50:
675
Appendix P
EDIT is another situation that requires user input. So you must supply an initial edit macro to provide the
input that would normally be supplied by the user. Be sure to end the macro with an ISREDIT END or
ISREDIT CANCEL statement. This ensures that the edit screen is not displayed. Otherwise, you could have an
infinite loop.
Summary
This appendix described three different ways to run your Rexx programs in batch on mainframes. Running as
MVS batch (using IRXJCL) is easy, but the Rexx programs don't have access to many commonly-used TSO
services. Running in the TSO/E environment (using IKJEFT01) remedies this. This approach works well for
many batch Rexx programs.
If the program needs ISPF services, run it using IKJEFT01 with ISPSTART. Be sure to allocate the several
ISPF libraries that are required in this case.
Credits: thank you to IBM for the coding examples in this appendix. See the IBM manuals TSO/E REXX Users
Guide, TSO/E REXX Reference, TSO/E User's Guide, and IBM Compiler and Library for REXX on IBM Z.
676
Rexx <--> CLIST Translation
These charts enumerate the equivalences between the Rexx and CLIST languages.
They are for those who know one of these languages and want to learn about the other. They may
also be useful for conversions.
(These charts are not about “which language is better.”)
The Basics
Rexx CLIST
Easy to learn, use, and
Yes Yes
maintain
Very powerful Yes Yes, but lacks common language features
Open source Yes No
Portable Yes No
Runs on all platforms Yes No
Runs as the mainframe shell No Yes
Interfaces to tons of tools Yes No (intended to issue OS commands)
Appendix Q
Profiles
Rexx CLIST
TRL-2, ANSI, Mainframe, ooRexx,
Dialects CLIST
NetRexx
* Default scripting language for
mainframes and several minor platforms
Unique Usage * Available on all z/OS mainframes
* Interfaces to all mainframe environments
and address spaces
Procedural, scripting, object-oriented
Programming paradigms Procedural, scripting
(ooRexx and NetRexx), functional
OOP: classes, objects, multi
inheritance, polymorphism, In ooRexx and NetRexx Unsupported
encapsulation
User Group Rexx Language Association SHARE
Quick Online Lookup RexxInfo.org IBM TSO/E CLISTs Manual
Forum RexxLA forum Expert Forum @ IBMMainframes.com
Further information RexxInfo.org IBM TSO/E CLISTs Manual
678
Rexx <--> CLIST
Language Comparison
679
Appendix Q
680
Rexx <--> CLIST
Sources include RexxInfo.org and the IBM manual TSO/E CLISTS. Also z/OS TSO/E REXX User's Guide
compares Rexx with Clists. Two Rexx books have comparison chapters on this topic: REXX in the TSO
Environment by Gabriel F. Gargiulo, and The REXX Handbook by Gabriel Goldberg and Philip Smith.
681
Mainframe Rexx <--> ANSI Rexx
These charts enumerate the exact differences between mainframe and ANSI-1996 standard Rexx.
They may be helpful in language conversions, or if you know one of these two language variants and
want to learn what is different in the other.
Profiles
Language Comparison
ANSI Rexx Mainframe Rexx
Requires "Rexx" in 1st line comment No Usually
ANSI Symbols (aka character set) Yes Yes, a superset
ANSI standards for case-insensitivity, free formatting,
Yes Yes
etc
684
Mainframe Rexx <--> ANSI Rexx
685
Appendix R
Supported by z/OS if the Stream I/O Package is installed, while VM supports this natively.
z/OS and z/VM only support this format: LINES([name])
LINES
z/VM: the function returns the number of completed lines remaining in the character input
stream.
686
Mainframe Rexx <--> ANSI Rexx
Sources include RexxInfo.org, and the IBM manuals TSO/E REXX Reference and REXX VM Reference.
687
How to Code with JSON
by Howard Fosdick and Mark Hessling
Overview
There many different formats for data interchange. One that has become very popular as JavaScript
has come to prominence is JavaScript Object Notation, or JSON.
JSON is:
□ An open standard for data interchange
□ Human readable
□ Self-descriptive (labels identify the different data elements)
□ Object based
□ Usable from any programming language
□ Simple to work with and widely used
JSON is one of several common data storage and interchange formats. Recall that Chapter 18
explored how the use the RexxXML package with Rexx. XML, or Extensible Markup Language, is an
alternative data interchange format to JSON.
XML and JSON are similar standards in that both are human-readable, self-descriptive, and
organize data into hierarchies. Each has its advantages. XML was originally designed for
describing documents. It supports namespaces, comments, and various encoding schemes.
JSON comes from a JavaScript heritage. It was designed to describe data objects and arrays. It’s
generally considered easier and quicker to read or parse than XML.
In this chapter, we’ll explore the Rexx/JSON interface. You can download this interface from
Sourceforge at https://sourceforge.net/projects/rexxjson/.
Background
JSON files are data objects that display as readable text files. They were originally designed for
transmitting and storing JavaScript objects. Today they are an international standard used by all
computer languages.
JSON values are data typed as: object, array, string, number, boolean, or null. Strings are enclosed in double
quote marks: "string_data". When stored to disk, JSON files typically have the extension: .json.
Rexx/JSON
The Rexx/JSON product is an external function package that lets your Rexx programs to manipulate JSON
data. The product includes about three dozen functions. About 20 of them help programs create, modify,
and maintain JSON objects and arrays. Another dozen focus on extracting data from JSON data objects. A
final half dozen functions help with outputting JSON data and performing administrative tasks.
Here’s a very simple Rexx/JSON program. It just creates a JSON data object, and displays it to the user:
Here’s sample output from a run of this script. You can see that it's a single object (identified by the
curly braces), and that it contains three name/value pairs. The first is a numeric data type, the second is
a string, and the last one is boolean:
{
"id": 1,
"message": "Hello",
"flag": true
}
690
How to Code with JSON
To explain the script, the first two executable lines make the Rexx/JSON external function library available to your
program. You routinely code these two lines at the start of every Rexx/JSON script:
my_obj = JSONCreateObject()
The next three lines add number, string, and boolean data items to that object, respectively. JSON data elements are
always typed, so you use JSONAddNumberToObject to add a number, JSONAddStringToObject to add a
character string, and JSONAddBoolToObject to add a boolean flag. In each of these functions, the first parameter
gives the object name, while the second and third parameters provide the name and value of the data elements.
Thus you create a name/value pair.
Each of these functions returns an internal object element identifier into the variable named item. An empty
return value signifies an error occurred, so this is where you would error-check in a program that's not just an
example.
The invocation of JSONPrint then displays the three data elements in the object my_obj. Notice how the output
respects JSON syntax: curly braces surround the object, and each element presents as aname/value pair, with string
values in double-quotation marks.
A Conversion Program
Another competing data standard to JSON is Comma Separated Values, or CSVfiles. You're probably familiar with
CSV files from spreadsheets, since users routinely export their data to them.
A CSV file consists of a list of data elements separated by commas. Sometimes you'll see strings surrounded in
quotation marks, but usually you won't. Like JSON files, CSV files are simple and human-readable. But JSON files
are object-oriented, whereas CSV files are not. CSV files have no notion of objects (or arrays for that matter).
They're simply a series of strings, separated by commas.
JSON identifies or tags each data element with its name. These are its name/value pairs. Thus, JSON files
are self-descriptive. CSV files only contain the data elements themselves, without labels of any kind. So JSON
is a bit more sophisticated in its structure.
Let's take a look at a simple program that reads a CSV file exported from a Microsoft Excel spreadsheet.
The program converts each record into a JSON object and transmits it.
The CSV input file is in this format. There may be from one to three phone numbers per record (that is, per
person):
691
Appendix S
Here's a short, three line sample input file:
Duck,Donald J.,[email protected],[email protected],555-1717,555-4751,555-7395
Roadrunner,King,[email protected],[email protected],555-1111,,
Pig,Porky,[email protected],[email protected],555-8787,555-0012,
The output from the program (below) shows how it has encoded this CSV data into three JSON object instances.
Each is identified by the required curly braces.
Within each object are two JSON arrays, denoted by brackets. The first is the email_array, which contains
two email addresses. The second is the phone_array, which contains from one to three telephone numbers.
Missing phone numbers are represented as null strings. They could have alternatively have been represented
the JSON null data type.
This screenshot shows the program's output. These are the data objects this program would transmit:
parse arg filename /* read the file name supplied on the command line */
say
call JSONDelete record obj
end
How to Code with JSON
The above program starts with the two mandatory lines that invoke access to the Rexx/JSON external
function library. The third line uses functions JSONVariable and JSONGetVersion to print the first
line you see in the program output. That line lists the version of the Rexx/JSON product.
The parse arg filename statement accepts the name of the CSV file to process from command line
input. Then, the do while loop will process each CSV file line, one at a time.
The parse linein instruction separates the input line into its constituent data items, recognizing
that commas delimit the data. Then the JSONCreateObject function creates an object instance to
contain the data from one line of CSV data.
The JSONADDStringToObject function adds the person's last name and first name as strings into the
object. Note that the JSON standard uses double quotation marks for strings (whereas it's common to
see either single or double quotation marks in Rexx).
The two JSONAddArrayToObject functions define the email and phone arrays as part of the object
record_obj. Then the two JSONAddStringToArray functions add the email and phone values to
the object. To illustrate different coding techniques, the first invokes JSONAddStringToArray as an
inline function, while the second one uses a call instruction.
Note that the JSONAddStringToArray function accepts multiple arguments, so we require only a
single invocation to add two email addresses, or to add multiple phone numbers.
In JSON, you must convert an object to a string to either transmit, display, or print it. This is referred
to as "stringifying" the object. The JSONStringify function performs this task.
The program simply displays the stringified records to the user. If you wanted to transmit them, this is
where that statement would be placed.
Finally, the program deletes the object via JSONDelete and ends.
You could alternately code this program by creating a single object at the start of the program, then
reusing that object (instead of continually creating and deleting objects for each record). You would use
functions like JSONDelete, JSONDeleteItemFromArray, and JSONDeleteItemFromObject to
delete elements from the JSON object in order to implement this change.
With Rexx/JSON, you can create a JSON data object in a program and then pass it into a subroutine that accesses it
with parse arg. The subroutine could manipulate the object's contents. Then the calling routine can access the
object's changed contents when the subroutine completes. Here's an example:
employee.!id = 10
employee.!name = 'Mark'
employee.!available = 1
employee object = JSONCreateObjectFromStem( 'employee.!' )
boss object = JSONCreateObject( 'id', 20, 'name', 'Kate' )
693
Appendix S
call JSONAddObjectToObject employee object, 'boss', boss object
call mysub employee object
say 'Still available? ' JSONGetObjectItemValue( employee object, 'available' )
call JSONDelete employee object
exit 0
mysub: procedure
parse arg object
count = JSONObjectToStem( object, 'stem.!' )
say 'Value of id is:' stem.!id
say 'Value of name is:' stem.!name
say 'Value of available is:' stem.!available
say 'Value of boss name is:' JSONGetObjectItemValue( stem.!boss, 'name' )
call JSONDeleteItemFromObject object, 'available'
call JSONAddBoolToObject object, 'available', 0
return
Summary
JSON is a very popular data interchange and storage format driven the dominance of JavaScript in web
programming. It's a simple human-readable format that offers an alternative to XML and CSV files, among
many other options.
This appendix walked you through a few example programs to orient you to Rexx/JSON programming. The
manual that downloads with the product offers all the detail you'll need to code more sophisticated
Rexx/JSON scripts.
Thank you to Mark Hessling for use of his code example, and for his years of leadership and support for key
Rexx tools including Rexx/JSON, the Regina Rexx interpreter, and a dozen others you can download at
www.Rexx.org.
694
Java Integration
By Howard Fosdick and Dr Rony G. Flatscher
Overview
Since its invention in the 1990s, Java has risen to rank as one of the world's most popular
programming languages.
Part of this popularity has to do with the wide range of supporting classes and methods available in
its vast free code repositories. The result is that many products feature Java automation "hooks" or
application programming interfaces (APIs).
Java has thus become the common vehicle to automate many program products. Examples span the
gamut from Android applications, to office suites like LibreOffice, to the SAP enterprise resource
planning product, and more.
Rexx fully integrates with Java. So all Java capabilities are available through Rexx. This chapter
describes two different ways to capitalize on Java's strengths and benefits with Rexx.
NetRexx
One way to integrate Rexx and Java is through NetRexx. As described in Chapter 30, the NetRexx
language was developed by Rexx's inventor, Michael Cowlishaw. It's a Rexx-like language that
features syntax similar to standard Rexx, but uses the Java Virtual Machine and fully integrates
with Java. In fact, NetRexx was the first language after Java itself to use the JVM.
NetRexx enables you to develop Java applications, servlets, Java server pages, and beans. It
employs all Java classes and methods. The advantage to NetRexx is that it allows you to script in a
simpler, Rexx-like language, instead of programming the more syntax-oriented and difficult Java.
You get the benefits of easier coding, higher productivity, and better assurance of code quality by
using NetRexx.
In sum, NetRexx makes Java easier. Chapter 30 provides more detail and a basic introduction to
the language.
Appendix T
Bean Scripting Framework for Rexx, orBSF4Rexx, allows you to use all Java's classes and methods from a classic
procedural Rexx interpreter, such as Regina Rexx.
Bean Scripting Framework for ooRexx, orBSF4ooRexx, enables you do the same when using Open Object Rexx. It
makes it possible to fully exploit Java and all its facilities while programming in ooRexx.
BSF4ooRexx is the more popular of the two products, since ooRexx is object-oriented and most who want to
leverage Java capabilities prefer object-oriented programming.
Installation
BSF4ooRexx comes in the form of an external Rexx function library. A base package called BSF.CLS supplies BSF and
its routines that camouflage Java as ooRexx, and thereby give ooRexx the ability to exploit all Java objects, classes,
and methods (regardless of their origins).
Note that BSF4ooRexx is a two-way bridge. It also allows Java programs to interact with ooRexx objects, that is, to send
messages to ooRexx objects.
The product runs with ooRexx in all its environments: Windows, Linux, and macOS. Be sure to read the installation
instructions for your particular platform.
The two prerequisites for BSF4ooRexx are Open Object Rexx, and a Java environment. Java can be either the Java Development
Kit (JDK)or the Java Runtime Environment (JRE), depending on your goals. Java can be downloaded from any of several
sources including Oracle Corporation at www.java.com, www.OpenJDK.org, and BellSoft at www.bell-sw.com.
JavaFX is recommended as a platform for developing and supporting web and desktop applications. It's available at most
websites that support Java downloads.
Once you've downloaded BSF4ooRexx, uncompress it ("unzip" it), and run its install script. The platform-specific
instructions with the product give you exact details. You may have to set a couple environmental variables, for example.
You'll notice that the product's directory structure is optimized to make installation and use as simple as possible.
For example, the library or libdirectory is where you could copy any Java archives (or .jar files) to ensure that
they're accessible. Documentation, sample code, and other items are clearly separated into their own subdirectories.
696
Java Integration
Examples
BSF4ooRexx ships with dozens of well-documented coding examples. We'll present just a
couple very simple ones here to give you a feel for how the product works. You'll easily learn
much more from the example code and documentation that ship with product itself.
These code snippets were all written by Dr Rony G. Flatscher, inventor of BSF4ooRexx and the key
developer behind it. They're all adopted from his many excellent tutorials on how to use BSF4ooRexx.
This first code snippet shows how you could create an instance of a Java class, in this case,
java.awt.Dimension. The BSF class helps you create this object using the required fully qualified
class name. The ooRexx script simply displays the string that the message toString returns.
Note how the ::requires directive loads the ooRexx to Java bridge.
java.awt.Dimension[width=100,height=200]
Here's an enhanced version of the program that shows how you can use Java fields as attributes in your
ooRexx code:
java.awt.Dimension[width=100,height=200]
java.awt.Dimension[width=1024,height=321]
697
Appendix T
This example shows how to import a Java class from within ooRexx with BSF4ooRexx. The imported Java class can
be used exactly as if it were an ooRexx class. Remember that you must refer to the fully qualified Java class name,
and that Java names are case-sensitive. But the rest of your coding -- in ooRexx -- is not case sensitive. So coding is
easier because you don't have to worry about case sensitivity in field and method names.
The first line in the code imports Java class java.awt.Color. Subsequent lines create a new color instance and then
render it brighter with the value that brighter returns:
red: java.awt.Color[r=255,g=0,b=0]
myColor: java.awt.Color[r=100,g=200,b=3]
brighter: java.awt.Color[r=142,g=255,b=4]
Here's a final example that invokes Java class createJavaArray to create and manipulate a Java array.
As you can see, you can use the Java array object in your ooRexx code exactly as if it were a Rexx array. Indices
start at 1, and you can use all ooRexx array methods like "AT", "[]", "PUT", "[]=", "supplier", and the like. You
can process the array with ooRexx logical constructs such as do ... over and do with ... over.
Potential Uses
The ways you can apply a generalized tool like BSF4ooRexx are limited only by your imagination. Some
examples shipped with the product show how to use it with:
Summary
Rexx rully integrates with Java, much more so than most programming languages. Combining Rexx with Java
gives you the best or both worlds: huge Java code repositories and all Java's capabilities, along with Rexx's
strengths in ease or use, coding, and sortware maintenance.
Nhere are two basic ways to meld Rexx and Java. ene is to use tetRexx, the first language arter Java to use the
Java Virtual Machine. tetRexx seamlessly integrates with Java in every way.
tetRexx is a "Rexx like" language in that it does not conrormto Rexx standards like NRL-2 and AtSI-1996.
However, Rexx programmers can pick up the language very quickly (it was designed with that very purpose in
mind). And classic Rexx scripts can easily be converted to tetRexx by the rree script Rexx2Nrx. Chapter 30
provides more inrormation and some simple programming examples.
Nhe other way to integrate Rexx and Java is to use epen ebject Rexx along with the Bean Scripting Framework
ror ooRexx, or BSF4ooRexx.
Nhis tool gives you rull access to all Java's repositories and capabilities rromwithin ooRexx. It's a two way bridge
-- ooRexx programs can use Java code and tools, and Java programs can use ooRexx code.
For more inrormation on BSF4ooRexx, simply download the product rrom its home at SourceForge at
https://sourceforge.net/projects/bsf4oorexx/ . Nhe download comes with a vast array or tutorial
documents and example scripts that demonstrate its use with many different tools.
699
Appendix T
Another good resource is Dr Rony G. Flatscher's website at https://ronyrexx.net/. It contains articles, tutorials,
application projects and more using BSF4ooRexx.
Finally, we mention the Rexx Language Association website at www.RexxLA.org. The Rexx LA is the organization
charged with formal responsibility for developing and supporting the BSF4ooRexx project.
Thank you to Dr Rony G. Flatscher for permission to use his code examples in this chapter, and for his years of
dedicated leadership of the BSF4ooRexx and BSF4Rexx projects.
700
The Secrets of PARSE
by Frank Clarke
Overview
PARSE is screamingly fast. Whatever needs doing, if it can be done by PARSE, it will be faster
than any alternative.
We will look at several instances of PARSE, concentrating on PARSE VALUE, the most flexible
parsing method of all. The format of a PARSE VALUE statement is:
PARSE VALUE <value-string> WITH <template>
zx = stem.0 + 1
stem.zx = 'stuff associated with zx'
stem.0 = zx
While there's nothing 'wrong' with that, consider what PARSE VALUE can do for it:
The value string is formed by adding 1 to the current value of stem.0 and then appending whatever is to be
stowed in the next element of the array. The template orders the first word of the value string to Lie assgn^ed to
variable zx and thee remainder to be loaded to stem.zx. The parsing position is then rcsc'f to 1 (the beginning of
the string) and the first word loaded to stem.0, discarding the remainder. The effect is exactly what is
accomplished by the 3-line example above. One PARSE eliminates three statements. It takes a bit of analysis the
first time one sees it, but after that, it will seem as natural as breathing.
GETVAL is used to provide a value associated with 'KEY' (if such exists). If such does not exist and
GETVAL returns a null, the next statement salts key_value wtth the default.
Alternatively, we may:
That is, if GETVAL returns a value, it is assigned to key_value and the remainder of the value-string is
discarded, but if GETVAL does not return a value, the only thing in the value-string will be the default value,
and that is assigned to key_value.
On the theory that "it's better to have and not need than to need and not have", traceability can be built-in to
code simply and easily using that same technique:
That is, the code looks in variable opts to find the string TRACE and whatever token follows it is loaded to
variable tv. If opts does not have a TRACE string, tv will be empty. The next fine supplies tvand a
default value for tv and loads the first of these back into tv. If tv was originally null because the first
PARSE didn't find it, there's still the default and that becomes the final value.
702
The Secrets of PARSE
Since we're talking tracing here, the next line turns the Trace off unconditionally and then turns it on
with whatever value is in tv. When the enclosing routine is invoked:
?r (interactive results) is set as the trace mode, and you can watch the program execute line-by-line. If
that trace ?r wasn't included in the parameter iist, the Trace will be N (normal -- effectively'off') and
the program will operate without tracing.
Initializations
It's common to see:
adlib = ''
batch = 0
common = ''
default = 121
excct = 0
To add more variables to either, just add them to the appropriate list, and if that list starts getting too long, change
the Copies vahie to a higher number.
because date and time values are guaranteed to be in-synch only if they are all captured in a single statement.
Summary
PARSE VALUE provides speed and flexibility for doing in a single statement many tasks that might otherwise
be done by coding several statements.
These examples show how you can leverage it for maximum efficiency.
703
Array I/O
Overview
Array I/O refers to reading or writing more than a single record to or from a Rexx array with a single
instruction. You could, for example, read a hundred records from a file into an array with a single
statement. Or, you could write those hundred records from a Rexx table to a file with a single
instruction.
You could even read an entire file into an array at once. Or write an entire dataset with one instruction.
Rexx does not formally support array I/O in its two key language definitions, TRL-2 or the ANSI-1996
standard. Nonetheless, array I/O is available to you, regardless of which Rexx interpreter you use.
This chapter explores array I/O. What are its advantages, and how do you code it? Let's get started.
Rexx Arrays
First off, recall how to code arrays. As Chapter 4 elucidated, Rexx implements arrays (or tables)
through the means of compound variables (or what many call stem variables).
A compound variable consists of a stem and a tail. A simple list or single dimension table can thus
be coded as: my_array.i. my_array. is the stem (note that it includes the period), while j is
the tail or subscript.
To perform array I/O, then, you read multiple records from a file into a stem in a single statement.
Or you write multiple lines from a stem to a dataset.
Appendix V
Advantages
Array I/O has several advantages.
First, remember that Rexx arrays are dynamically sized. This is perfect for working with files of unknown
size. Programming languages that only support preallocated, statically sized arrays do not fit well with
array I/O because they lack flexibility. Dynamically-sized arrays allow you to read in any number of
records in a single statement and the array will expand as necessary to receive the data.
Second, remember that for most implementations, the size of a Rexx array is limited only by the size
of available memory. So reading in an entire dataset and processing it as an array can be both
convenient for you as a programmer, and also more machine-efficient.
Finally, remember that Rexx supports a powerful use of arrays usually referred to as associative arrays or
content-addressable arrays. That is, you can subscript into your tables with character string values, rather
than just numbers. Array I/O reads or writes arrays to/from files based on consecutive array positions. But
that's not to say you can't transpose or treat the array as a content addressable structure. This allows for
highly efficient algorithms for solving certain kinds of problems. (Examples in this book of associative
arrays are the "telephone code look-up" program in Chapter 4, and the "command help" program in
Chapter 14.)
□ I/O may be more efficient for some platforms (as opposed to reading & processing one record at a time)
□ File locking may be minimized (locks need not be held during a loop that includes processing time)
Let's explore those potential advantages for file locking and maximizing I/O operations. Traditionally, computer
programs that process all the records in a file employ an algorithm something like this:
Open the file to read from, and Open the file to write to
While there is another record to process
Read the record
Process the record
Write results for that record
End
Close the input and output files
706
Array I/O
Compare that algorithm to one that reads and writes an entire file in single statements using array I/O:
You can see the potential advantages here. Both the input and output files are only used (and locked) for the
minimal possible time. Any time required for processing records is excluded from that time period.
Further, the operating system is free to optimize large I/O operations, instead of being told to (logically) operate on
a single record at a time. Of course, the savings here -- if any -- are platform dependent. Different operating systems
and their underlying hardware handle I/O blocking and buffering in different ways. But the fact remains that for
some platforms, this second algorithm makes for faster I/O as well as minimizing file locking.
RexxUtil -- Recall that Chapter 13 on "Writing Portable Rexx" on pages 206-207 described an external
function package named RexxUtil. This cross-platform package runs on all major operating systems with
most Rexx interpreters.
RexxUtil includes a collection of ten stem manipulation routines. These enable reading and writing a file
into/from a stem with a single function.
Related functions support sorting, searching, and copying the array with single statements, as well as for
inserting and deleting elements. There's even a RegStemDoOver function that simulates the construct do x
over y. This makes it easy to code array processing.
EXECIO -- On mainframe operating systems, the EXECIO instruction implements array I/O.
Appendix N described EXECIO in detail and provided coding examples. EXECIO allows you
to read or write any number of records in a single statement, from a single record all the way
up to all those in the entire file. The size of memory is about the only limitation you'll face in
using EXECIO.
707
Appendix V
The first example below reads 100 records into the array mydata. The second statement reads an
entire file into the array myarray, and closes the file after the operation:
Open Object Rexx -- ooRexx provides array I/O through built-in methods in its Stream Classes. Methods
like arrayIn and arrayOut implement it.
ooRexx also has specialized array processing features. For example, the do over construct makes
array processing more convenient. And the Array Class itself includes a couple dozen supporting
methods.
BRexx -- As mentioned in Chapter 22 on BRexx, this interpreter ships with its files.r library. Pages
374-375 discuss how this implements array I/O and include a sample program.
These example lines show how to read or write an entire array in single lines:
Address Instruction -- Finally, recall that the address instruction allows coding stems for input, output,
and error results from system commands. This feature was added in the ANSI-1996 standard. (See page 216
for discussion.)
Summary
Whichever Rexx interpreter you use, and whatever platform you run it on, array I/O is available to
you. Array I/O can potentially make better use of machine resources from the standpoint of I/O
operations and resource locking.
Perhaps more importantly, array I/O is easy to program, makes for more readable code, and in some
cases can enable better algorithms and more efficient use of machine resources.
708
w
Interview Questions
Overview
This appendix offers two sets of questions for Rexx job interviews. The first test covers generic Rexx.
The second specifically covers mainframe Rexx.
You should be able to complete each test in about a half hour. This is a "from memory" test only - no
cell phone or computer access allowed. The answers appear after the two tests.
25 or more questions answered correctly (about 75%) indicate a credible job candidate. Less than 20
questions correct indicate a candidate who may not be credible.
social_security_number = '499-55-1212'
PARSE VAR social_security_number comp1 '-' comp2 '-' comp3 .
4. How many characters does the CHARIN function return by default? Can it move the read pointer
without reading any characters?
9. How do you set the arithmetic precision - the number of significant digits for calculations ?
10. What is the difference between the equal to ( = ) and strictly equal to ( == ) operators?
11. What is the remainder divide operator and why would you use it?
12. What would you encode to uninitialize (or “unassign”) two variables named ONE and TWO?
13. What is the difference between the PUSH and QUEUE instructions?
14. What instruction could you code to leave an endless loop (a “do forever” loop) and proceed to the next
statement following the DO loop?
15. On a Windows server, you’ve been put in charge of project to migrate hundreds of classic Rexx programs to
Open Object Rexx (ooRexx). All programs you will migrate strictly conform to the ANSI Rexx standard. Would
you expect that:
(A) Zero program changes will be necessary because ooRexx runs any classic Rexx program
(B) The vast majority of programs will run without changes, but you could run into an exception
(C) You’ll have to rewrite most of the classic Rexx programs to run under ooRexx
(D) ooRexx is not compatible with classic Rexx. You’ll have to recode all the programs.
16. What does the ARG function do when processing input(s) given by a Rexx command (that is, a Rexx
script run from the command line) ?
710
Interview Questions
17. You’re downloading a Rexx script from the web that you want to run on your computer. But you notice that your
keyboard has no symbol for this one that occurs in the program: . What symbol could you code instead of ?
18. How do you code a binary string constant within a Rexx program?
19. Between the Rexx comparison, arithmetic, and concatenation operators, which has highest priority?
20. For the compound variable List.j what is the stem, and what is the tail (also called the subscript or index)?
21. How can you override the default precedence order of operations in a Rexx statement?
22. What are the OR and EXCLUSIVE OR operators, and what is the difference in how they operate?
25. When you code a Rexx statement in your program, how can you continue it to a second line?
27. When you issue a command to an external environment from your Rexx program, how do you know if that
command worked?
28. You have an error number (error code), and you want to find out what it means. How can you do this from
within a Rexx program?
29. Which of these is not one of the standard Rexx keywords for trapping errors: ERROR, FAILURE, HALT,
NOTVALID, and SYNTAX?
30. In this program, which statement will display output that differs from that of the other two statements?
711
Appendix W
31. Does the PULL instruction alter input data? Does PARSE PULL?
33. How do you initialize all elements of the compound variable stem (aka, an array) named MY_ARRAY. to 0?
social_security_number = '499-55-1212'
PARSE VAR social_security_number comp1 '-' comp2 '-' comp3 .
2. What command can you use to define a new disk dataset for output from a Rexx program running under
TSO when you want to specify such parameters as record length, block size, and more?
4. You have a disk dataset consisting of 20 data elements. At a minimum, how many EXECIO statements do
you need to read the entire dataset?
9. How do you set the arithmetic precision - the number of significant digits for calculations - in a Rexx
program?
10. What is the difference between the equal to ( = ) and strictly equal to ( == ) operators?
11. What is the remainder divide operator and why would you use it?
712
Interview Questions
12. What word is required in the first line of a Rexx program for some execution environments?
13. What is the difference between the PUSH and QUEUE instructions?
14. What instruction could you code to leave an endless loop (a “do forever” loop) and proceed to the next
statement following the DO loop?
15. What TSO external function would you code to get information about a dataset?
17. How can you enter an immediate command from within TSO?
19. Between the Rexx comparison, arithmetic, and concatenation operators, which has highest priority?
20. For the compound variable List.j what is the stem, and what is the tail (also called the subscript or index)?
21. How do you code a binary string constant within a Rexx program?
22. What are the OR and EXCLUSIVE OR operators, and what is the difference in how they operate?
25. When you code a Rexx statement in your program, how can you continue it to a second line?
27. You want to speed up the execution of a TSO Rexx program you’ve just written - without changing the
program code. What do you do?
28. You have an error number (error code), and you want to find out what it means. How can you do this from
within a Rexx program?
713
Appendix W
29. You’re writing a Rexx program that interfaces to a Db2 table. How can you process each row from the
table, one at a time?
30. Can you call Db2 database stored procedures from within a Rexx program? How?
31. Does the PULL instruction alter input data? Does PARSE PULL?
33. How do you initialize all elements of the compound variable stem (aka, an array) named MY_ARRAY. to 0?
In other words, comp1will contain 499, comp2 will contain 555, and comp3 will contain 1212.
3. Answer: Three special variables are RC, RESULT, and SIGL. RC contains a return code from a command,
or a syntax error code. RESULT contains a value passed back from a RETURN instruction in a subroutine.
(If a RETURN is encoded without an operand, RESULT is set to unitialized.) SIGL identifies the line
number of last instruction that caused a jump to a label.
4. Answer: The CHARIN function reads one character by default. Yes, you can use CHARIN to position the
read pointer without actually reading any characters. For example, CHARIN(‘TEXT_FILE’,1,0) positions the
read pointer to the start of TEXT_FILE without reading any characters.
5. Answer: To enable one of SIGNAL’s supported error conditions, and optionally specify its associated
routine. Or to disable that error condition. (It’s not recommended, but some also use it to simulate the
action of a GOTO statement.)
6. Answer: To set the execution environment for command(s) sent out from a Rexx program.
7. Answer:To tell your Rexx program what the current external command execution environment is.
714
Interview Questions
8. Answer: IF and SELECT. (Note that WHEN is a keyword or clause within the SELECT statement; it is not a
Rexx instruction.)
10. Answer: Strict comparison requires that two strings be identical (leading and trailing blanks count in the
comparison.)
11. Answer: The operator is: // . You use it when you want to obtain the remainder of a division operation.
Or you could encode a pair of DROP instructions: DROP ONE ; DROP TWO ;
13. Answer: PUSH adds an element to the queue or data stack in the order Last-In, First-Out (LIFO). QUEUE
adds an element to the queue or data stack in the order First-In, First-Out (FIFO).
14. Answer: The correct answer is the LEAVE instruction. It alters the flow of control from within a DO loop to
the statement following its END clause. Instructions like EXIT and RETURN will break out of the endless loop,
but do not direct execution to the code following the DO loop. (You could also use SIGNAL for this purpose but
this is poor programming practice.)
15. Answer: Generally speaking, ooRexx runs classic Rexx programs that conform to the ANSI-1996 Rexx
standard without any changes. However, there are rare exceptions (ooRexx lacks a couple new items from that
standard.) With “hundreds” of programs involved, you might well run into a few of the exceptions. The best
answer is B.
16. Answer: Answer (C) is correct. (A) is incorrect because it tells what the ARG instruction does, not the ARG
function. (B) is incorrect because it tells what ARG does when encoded to parse arguments in a Rexx subroutine
or function. It does not tell what happens when ARG processes command line arguments (parameters encoded
when Rexx is invoked from the command line). (D) is just plain wrong.
17. Answer: is the “not” or negation symbol. If your system doesn’t support it, just use a backslash instead: \ .
For example, instead of coding “not equal” as -= you would code it as \= .
715
Appendix W
18. Answer: Code it as a string of 0’s and 1’s within single or double quote marks followed by the letter ‘b’ or
‘B’. Examples: '0101'b or "0101"B .
20. Answer: The stem is List. (it includes the period), and the tail (or subscript or index) is j
21. Answer: Use parentheses. Enclose the higher precedence operations you want executed first within the
parentheses.
23. Answer: You code PROCEDURE EXPOSE to ensure that only the specified list of variables is available (or
“exposed”) to a subroutine. The subroutine can both read and update any variable(s) that are exposed to it.
24. Answer: The NOVALUE condition is raised when the NOVALUE error condition is enabled, and a variable
is referenced that has not yet been initialized.
26. Answer:It returns the setting of NUMERIC DIGITS (it returns the numeric precision).
27. Answer: Inspect the value of the special variable RC. It contains the command’s return code.
28. Answer: One way is to use the ERRORTEXT function. It returns the error text associated with a given error
number.
29. Answer: NOTVALID. For ANSI-standard classic Rexx the trap conditions are ERROR, FAILURE, HALT,
NOTREADY, NOVALUE, SYNTAX, and LOSTDIGITS.
716
Interview Questions
onetwo
onetwo
one two
The answer is statement (C). It outputs the two variables with an intervening space (or blank).
Statement (A) is concatenation by abuttal, while statement (B) uses the concatenation operator. Both
output the two variables with no intervening space (or blank).
31. Answer: PULL automatically translates characters to their uppercase equivalents. PARSE PULL does not
alter the data by performing uppercase translation.
32. Answer: The variable item_one contains the first data element parsed from the string called my_string,
item_two contains the second, and the period ignores all subsequent data items. If the period were not encoded,
item_two would contain the second and all subsequent input data elements concatenated together.
4. Answer: 1. One EXECIO statement can read the entire dataset into an array or table (more accurately called a
compound variable), or onto the stack.
5. Answer: To enable one of SIGNAL’s supported error conditions, and optionally specify its associated routine.
Or to disable that error condition. (It’s not recommended, but some also use it to simulate the action of a GOTO
statement.)
717
Appendix W
6. Answer: To set the execution environment for command(s) sent out from a Rexx program.
7. Answer: To tell your Rexx program what the current external command execution environment is.
8. Answer:IF and SELECT. (Note that WHEN is a keyword or clause within the SELECT statement; it is not
a Rexx instruction.)
10. Answer: Strict comparison requires that two strings be identical (leading and trailing blanks count in the
comparison.)
11. Answer: The operator is: // . You use it when you want to obtain the remainder of a division operation.
12. Answer: The word REXX placed inside a comment. Example: /* REXX */
13. Answer: PUSH adds an element to the queue or data stack in the order Last-In, First-Out (LIFO). QUEUE
adds an element to the queue or data stack in the order First-In, First-Out (FIFO).
14. Answer: The correct answer is the LEAVE instruction. It alters the flow of control from within a DO
loop to the statement following its END clause. Instructions like EXIT and RETURN will break out of the
endless loop, but do not direct execution to the code following the DO loop. (You could also use SIGNAL
for this purpose but this is poor programming practice.)
15. Answer:LISTDSI
17. Answer: In TSO, you press the attention interrupt key to enter attention mode. Then you can enter your
immediate command (such as HE, HT, HI, etc).
20. Answer: The stem is List. (it includes the period), and the tail (or subscript or index) is j
718
Interview Questions
21. Answer: Code it as a string of 0’s and 1’s within single or double quote marks followed by the letter ‘b’ or ‘B’.
Examples: '0101'b or "0101"B
23. Answer: DBCS stands for the Double Byte Character Set. In DBCS, each character is represented by two bytes
(instead of one). You might use it for working with languages that contain many unique characters, for example,
Chinese or Japanese.
24. Answer: The NOVALUE conditions is raised when the NOVALUE error condition is enabled, and a variable
is referenced that has not yet been initialized.
26. Answer: It returns the setting of NUMERIC DIGITS (it returns the numeric precision).
28. Answer: One way is to use the ERRORTEXT function. It returns the error text associated with a given error
number.
29. Answer: Declare a cursor. Then use it to retrieve and process the rows one by one.
30. Answer: Yes, just use the CALL command, addressed to Db2.
31. Answer: PULL automatically translates characters to their uppercase equivalents. PARSE PULL does not alter
the data by performing uppercase translation.
32. Answer: The variable item_one contains the first data element parsed from the string called my_string,
item_two contains the second, and the period ignores all subsequent data items. If the period were not encoded,
item_two would contain the second and all subsequent input data elements concatenated together.
Thank you to RexxLA members who helped design and debug these questions: Frank, Shmuel, David, Wayne,
Rob, Mark, Chip, Walter, and others.
719
Rexx <--> Bash Translation
These charts enumerate equivalences between Rexx and Bash (Bourne-Again Shell).
They are for those who know one of these languages and wish to learn about the other.
(These charts are not about “which language is better.”)
The Basics
Rexx Bash
Easy to learn, use, and
Yes No (unfriendly syntax)
maintain
Very powerful Yes Yes
Open source Yes Yes
Portable Yes Yes
Runs on all platforms Yes Yes, major platforms
Runs as the OS Shell No * Yes
Interfaces to tons of tools Yes Yes
ANSI or ISO Standard Yes (ANSI-1996) Yes (POSIX compliant with extensions)
*Regina Rexx can run in the same process as the Z shell (zsh)
Appendix X
Profiles
Rexx Bash
TRL-2, ANSI, Mainframe, ooRexx,
Dialects Bash (a superset of the Bourne shell)
NetRexx
* Default scripting language for
Linux (including on
* Default scripting language for
Windows/Linux and Linux on Z
mainframes and several minor platforms
Unique Usage mainframes)
* Interfaces to all mainframe environments
* Sometimes the default for BSD,
and address spaces
Oracle Solaris, older Apples, other
systems
Procedural, scripting, functional, object-
Programming paradigms Procedural, scripting, functional
oriented (ooRexx and NetRexx)
OOP: classes, objects, multi
inheritance, polymorphism, In ooRexx and NetRexx Unsupported
encapsulation
User Group Rexx Language Association Free Software Foundation
Quick Online Lookup RexxInfo.org DevHints.io, LinuxTutorials.org
Cheat Sheet
LinuxSimply.com,
(printable PDF)vm/cms RexxInfo.org
Cheatography.com
handbook
Forum RexxLA forum LinuxQuestions.org, Linux.org
Free Software Foundation: GNU
Further Information RexxInfo.org
Bash
722
Rexx <--> Bash
Language Comparison
723
Appendix X
One-dimensional arrays:
Collections of Variables Use compound variables
declare -a array_name
Associative Arrays Use compound variables declare -A array_name
Multidimensional Arrays Use compound variables Unsupported
No generalized facility (offers a
Yes (push, pull, parse pull, queue,
Stack & Queue Operations directory stack with pushd, popd,
queued)
and dirs)
Decimal Arithmetic Default Install and use: bc
if, do, select, call, exit, return, iterate, until, while, for, break, continue,
Flow of Control
leave, signal, nop return, exit
Use the -x option.
Trace Script Execution trace (instruction), trace (function) Run the script: bash -x
or inside the script: set -x
Terminate Process exit exit
say "Enter your name:" echo -n "Enter your name: "
Get User Input
parse pull name read name
Exception Handling signal trap
novalue, error, failure, halt, notready, trap covers 64 signal specs
Standard Exceptions
syntax, lostdigits (list them by: trap -l)
Just issue the command string
Run an Operating System
(Rexx passes unrecognized strings to the Just issue the command string
Command
default active environment)
724
Rexx <--> Python Translation
These charts enumerate equivalences between the Rexx and Python languages.
They are for those who know one of these languages and wish to learn about the other.
(These charts are not about “which language is better.”)
The Basics
Rexx Python
Easy to learn, use, and maintain Yes Yes
Very powerful Yes Yes
Open source Yes Yes
Portable Yes Yes
Runs on all platforms Yes Yes
Interfaces to tons of tools Yes Yes
ANSI or ISO Standard Yes (ANSI-1996) No (de facto standard by PEPs)
Appendix Y
Profiles
Rexx Python
Version 2.x, Version 3.x, specialized
Dialects TRL-2, ANSI, Mainframe, ooRexx, NetRexx
implementations
* Default scripting language for mainframes * Most popular programming
and several minor platforms language in the world
Unique Usage
* Interfaces to all mainframe environments * Most widely taught language
and address spaces * Ships with many OS's
Procedural, object-oriented (ooRexx and Procedural, object-oriented,
Programming paradigms
NetRexx), functional, scripting functional, scripting
OOP: classes, objects, multi
inheritance, polymorphism, Yes, in ooRexx and NetRexx Yes
encapsulation
User Group Rexx Language Association Python.org Community
Quick Online Lookup RexxInfo.org QuickRef.me/python
Cheat Sheet
RexxInfo.org Cheatography.com
(printable PDF)
Forum RexxLA forum Python.org forums
Further information RexxInfo.org Python.org
726
Rexx <--> Python
Language Comparison
727
Appendix Y
This Language Comparison Chart maps equivalences between ANSI-1996 Rexx and Python 3.12. Some
have suggested that a more appropriate comparison would be between Open Object Rexx and Python.
Perhaps. But remember, this chart is not about "which language is better" or "which language has more
features." It's meant simply as an aid to those (like myself) who have occasion to work in both these
languages.
Sources: RexxInfo.org. Python Tutorial and Python Language Reference (v 3.12.3) from docs.python.org.
728
BRexx/370 for Mainframe Emulation
Overview
BRexx/370 (or BREXX370) is a port of the BRexx interpreter to mainframe operating systems. It's a free
and open source Rexx for IBM mainframe software based on the BRexx interpreter described in
Chapter 22.
BRexx/370 is an alternative to the IBM licensed REXX interpreter distributed with IBM operating
systems in the mainframe OS, VM, and VSE families.
The three mainframe architectures Hercules emulates are: System/370, ESA/390, and the latest 64-
bit z/Architecture. These three designs underlie all of IBM's mainframe operating systems.
So with Hercules, you could run almost any IBM mainframe operating system on your personal
computer. You can "run your own personal mainframe" on your Windows or Linux PC.
Appendix Z
There is a fly in this magical ointment, however. IBM carefully controls how it licenses its mainframe
operating systems. It does not make current ones available for equipment other than their mainframes.
Thus Hercules users must choose from a set of older IBM operating systems that are either public
domain or "copyrighted software provided without charge." Candidates include OS/360, DOS/360,
DOS/VS, MVS, VM/370, TSS/370, MUSIC/SP, Multics, and MTS. Other public domain options
include Linux on IBM Z and OpenSolaris for System z.
Many Hercules users have settled upon two older but free and legal mainframe operating systems: CMS
(VM/370) and MVS (OS/VS 3.8). These two operating systems are the direct ancestors of z/VM and z/OS,
respectively. VM/370 dates from the 1970s. It was the last free version of VM, from the days when IBM
gave the software away for free when you bought one of their mainframes. MVS 3.8 was in use until the
mid-1980s.
The diagram below suggest how the BRexx/370 stack might look running on Hercules.
BRexx/370
Both VM/370 and MVS 3.8 date from before IBM distributed Rexx. So, the question is: how does one
obtain Rexx for them? The answer was obvious: port an open source Rexx.
730
BRexx/370
The BRexx interpreter described in Chapter 22 was well-suited to the challenge. It required
some adaptation, which is why it's called BRexx/370. That's also why it's distributed through
separate BRexx/370 projects at GitHub (instead of with the main BRexx project).
Several GitHub projects supply BRexx/370. Please read through the rest of this chapter before
you decide how to install it.
One approach uses CMS (VM/370). You can download the VM/370 operating system from any of
several sources. Perhaps the place to start is the VM Community Edition website at
http://www.vm370.org/ .
Another BRexx/370 GitHub project targets MVS (OS/VS 3.8). The home page for this BRexx/370
distribution at GitHub is https://github.com/mvslovers/brexx370.
BRexx/370 for MVS is well documented. It comes with a User's Guide, Installation Guide, and
documentation for callable external functions, VSAM, array functions, and 3270 screen formatting.
BRexx/370 for MVS interfaces to a long list of MVS facilities, including VSAM/KSDS, TCP/IP, NJE38,
ISPF, ADDRESS TSO, ADDRESS MVS, ADDRESS COMMAND, ADDRESS LINK, 3270 screen
formatting services, access to external functions, and more.
The MVS BRexx/370 project is a part of a larger project called MVS 3.8 TK (the "TK" stands for
TurnKey). This project includes the MVS 3.8 operating system, many OS subsystems, and BRexx/370. It
all runs on the Hercules emulator.
Both MVS 3.8 TK and BRexx/370 are distributed in a single download. This makes for a simple install
of this complex software. The GitHub project home page for MVS 3.8 TK is at
https://github.com/patrickraths/MVS-TK5. The driving website for this software is
https://www.prince-webdesign.nl/tk5. You'll find much more detail there. There's also a
GibHub project that supplies any of several Docker images for mainframe systems including
BRexx/370 at https://github.com/RattyDAVE/docker-ubuntu-hercules-mvs.
Uses
Most who script with BRexx/370 do it because they enjoy controlling their own personal mainframe.
They're hobbyists exploring mainframe software in a way that once wasn't possible without buying a
mainframe.
731
Appendix Z
Beyond hobbyist interest, mainframe emulation with Rexx offers some practical uses as well. For
example: training. One can become familiar with what it's like to work with 3270 style screens and
consoles, and with a vast array of supporting systems. In addition to Rexx, there is Job Control
Language, Assembler, VSAM data access, NJE38, and much more. The fact that the operating
system is not current, or that some pieces may be missing (for example, Db2 or CICS) doesn't
diminish the value of what is offered.
Summary
Using the Hercules emulator, individuals can host CMS VM/370 or MVS 3.8 on their personal
computers. BRexx/370 fulfills the need for a free and open source Rexx interpreter for these systems.
The CMS and MVS implementations of BRexx/370 differ in small ways from each other, as well as
from IBM's mainframe Rexx. Chapter 29 and Appendices E and R discuss IBM mainframe Rexx.
They enumerate exactly how IBM mainframe Rexx differs from ANSI-1996 standard Rexx.
You can find several active Hercules discussion groups at https:/groups.io. At that website,
just click on "Find or Create a Group" and search for "Hercules".
Thank you to the Rexx Language Association members who contributed to this appendix with their
insights and documentation.
732
Index
Index
734
A- B
Index
first element of, as 0 or 1, 57
indexing, Reginald features for, 397 beaming, 441
initializing, 55, 56-57 Bean Scripting Framework, 615, 695-700
NetRexx, 522 beep function
number of elements in, storing,57 Regina, 338, 574
processing all elements in, 56 Reginald, 399
redirecting command I/O to and from, 216-7 bifurcation, 79-80
referencing uninitialized element of, 55 binary numbers, 26
sorting, in Reginald, 396 binding variables, 247, 249-250
sparse, 53 bit function, Reginald, 399
array I/O 374-375, 705-708 bit manipulation functions, Regina,336-337
asin function bit map indexes, 97
BRexx, 364 bit string functions, 96-97
Rexx/imc, 349 bitand function, 97, 106-107, 549
ask special name, NetRexx, 522, 524, 629 bitchg function, Regina, 336, 574
assignment statements, 25, 26 bitclr function, Regina, 336, 574-575
associative arrays, 54, 60-62 bitcomp function, Regina, 336, 575
Associative Arrays for Rexx package, 615 bitor function, 97, 549
associative memory, 54. See also arrays bitset function, Regina, 337, 575
asterisk (*) bittst function, Regina, 337, 575-576
multiplication operator, 27 BitVector class, roo!, 455
*-* in trace file, 138 bitxor function, 97, 549-550
asterisk, double (**), raise to a power operator, 27 blank lines, 23
atan function Bochs emulator, 424
BRexx, 364 books. See publications
Rexx/imc, 349 box style comments, 22
attributes (variables), Open Object Rexx, 465 braces ({}), vector class reference, 454
a2u function, BRexx, 368 brackets ([])
AuroraWare! tool, r4 and roo! interpreters, 450 adding or retrieving from collection, 470
in array indexes, 397
735
B-C
BSD platforms, 8 CGI (Common Gateway Interface)
BSF4ooRexx 695-700 BRexx library for CGI-scripting functions, 367
B-tree (balanced tree), 63 definition of, 273
buffers, relationship to stacks, 167-168 Internet/REXX HHNS WorkBench, 276-281, 616
buftype function Reginald support for, 388
Regina, 342, 576 Reginald tutorial for, 392
Reginald, 396, 398 Rexx/CGI library (cgi-lib.rxx), 274-276, 615
built-in functions, 110, 120. See also cgidie function, CGI/Rexx, 274
functions built-in subroutines, 41. See also CgiEnd function, HHNS WorkBench, 278
subroutines cgierror function, CGI/Rexx, 274
CgiHref function, HHNS WorkBench, 278
b2x function, 105, 550
CgiImg function, HHNS WorkBench, 278
byte-oriented I/O. See character-oriented I/O
CgiInit function, HHNS WorkBench, 277
cgi-lib.rxx library (CGI/Rexx), 274-276, 615
C CgiRefr function, HHNS WorkBench, 278
CGI/Rexx, 274-276, 615
C Developer’s Kit for Reginald, 388
>C> in trace file,139 CGI-scripting functions, BRexx library for, 367
CALL construct, 34 CgiSetup function, HHNS WorkBench, 277
call import function, BRexx, 363 CgiWebit function, HHNS WorkBench, 278
call instruction changestr function
definition of, 41-43, 112, 185, 537-538 definition of, 90, 550-551
error trapping with, 144-146, 152-154 mainframe Rexx, 504
example using, 70 Open Object Rexx,472
as structured construct, 34 roo!, 454
when to use, 71 character encoding stream, 96
call off instruction, 148 character folding, 97
call on instruction, 148 character map, 96
Callable service library (CSL) routines, 505 character sets, affecting portability, 203
Callback class, roo!, 455 character strings, 23.See also literals
callback function, roo!,454 character-oriented I/O, 68, 72-75, 191
callbacks, Rexx/DW, 264 CharacterVector class, roo!, 455
call- level interface (CLI), 230 charin function
camel case, 171 definition of, 72, 73, 551
capitalization, using effectively,170-171 return string for, 180, 196
caret,double (AA), instance creation,453 charout function
caret (A), method invocation prefix, 453 definition of, 73, 551
CASE construct, 34. See also select example using, 74
instruction case sensitivity explicitly controlling file positions, 72
automatic uppercase conversion, 186 return string for, 180, 196
capitalization, using effectively, 170-171 chars function
of Rexx language, 22-23 definition of, 73, 552
catch...finally instructions return string for, 180, 196-197, 205
NetRexx, 521 chdir function
roo!, 454 Regina, 338, 576
c2b function, Regina, 337, 576 Reginald, 391
c2d function, 105, 553 Rexx/imc, 348, 352
cd function, Regina, 338, 576 Chill tool, r4 and roo! interpreters, 451
cell phones, 435-445 chkpwd function, CGI/Rexx, 274
center function, 89, 550 Christensen, Anders (developer of Regina), 312, 331
centre function, 550
CFLOW tool, r4 and roo! interpreters, 451
736
Index
C
commands in other environments, issuing, 220-221
commands, OS
CICS (Customer Information Control System), 323 affecting portability, 191-192, 201-202
C-language interface, for RexxXML,294 building string for, 212
C-language I/O functions capturing output from, 213-215, 216-218
BRexx, 364-365 directing input lines to, 214, 216-218
Rexx/imc, 351, 354-356 environment for, 211, 216-217, 220
Class class, Open Object Rexx, 469, 620 environmental functions, Regina, 338
::class directive, Open Object Rexx, 470 environmental functions,
class hierarchies, Open Object Rexx, 466-467 Rexx/imc, 348, 352-354
class instruction error trapping for, 213, 216-218
NetRexx, 521, 630 example using, 218-219, 222-225
roo!, 453 issuing, 211-212
classes issuing, with Reginald, 393-394
NetRexx, 520 issuing, with VM Rexx, 496
Open Object Rexx, 465-467, 468-469, 476, 619-2 quotes enclosing, 181-182
roo!, 452, 455-456 result of (return code), 154, 211, 213
cleanquery function, CGI/Rexx, 274 comment delimiters (/*...*/), 22
clear command, Linux or Unix, 45 comments
CLI (call-level interface), 230 guidelines for, 176-177
Clist, 677-681 in roo!, 454
Clipboard class, roo!, 455 syntax for, 22, 175-176, 184
clipboard function, BRexx, 368 in VM Rexx, 494
close function commercial Rexx interpreters, 322-323
BRexx, 365 Common Gateway Interface. See CGI Common
Regina, 339, 577 Programming Interface (CPI), 505
Rexx/imc, 351 Comparator class, roo!, 455
clreol function, BRexx,368 compare function, 90, 552
clrscr function, BRexx,367 comparison operators, 28-30, 187
cls command, Windows,45 compilers. See mainframe Rexx
CMD environment, Reginald, 393-394 definition of, 323-324
CMS assembler macros and functions, 505 VM Rexx, 498-499
CMS commands, VM Rexx, 498, 499, 506-511 compound comparisons, 30
CMS functions, HHNS WorkBench, 278 compound symbols (array references), 54-55
CMSFLAG function, VM Rexx, 498 compound variable name, 25
Code Comments community, 531 compress function, Regina, 337, 577
code pages (character sets), affecting portability, 203 concatenation, 30-31, 79-80
code reviews, 183-184 concatenation operator (||), 30-31, 80
collection classes, Open Object Rexx, 468, 481-485 concurrency, Open Object Rexx, 468, 489-491
Collier, Ian (developer of Rexx/imc), 312 condition function, 154, 155-156, 197, 552-3
colon, double (::), preceding directives, 465, 470 condition trapping. See error trapping
colon (:), following a label, 42 conditions. See error conditions
column-position files, 86
Console class, roo!,455
comma (,), line continuation character, 111, 122, 173, CONSOLE interface,505
console I/O, BRexx library for, 367
186
constants, 54
comma-delimited files, 86, 691-2
constructs (control structures), 33, 34, 47
command identifier operator (!),454
command line, passing parameters on, 115-6, 185 content-addressable (associative) arrays, 54, 60-62
command procedure language, Rexx as, 309 ContextVector class, roo!, 455
command procedures control structures (constructs), 33, 34, 47
definition of, 209-210 conversational I/O, 75
example using, 222-225 conversions between numeric representations, 105-7
737
C-D
convertdata function, Reginald, 399
copies function, 90, 553 issuing SQL statements, 233, 250-1
COPYFILE command, 510 relational, 229
copyfile function tables, creating and loading, 239-241
BRexx, 368 tables, selecting results from, 241-244, 245-247
Reginald, 391 tables, updating, 243-244
cos function transactions, 233
BRexx, 364 datatype function, 88, 90-92, 105-106, 454, 554
Rexx/imc, 349 date function, 196,555
cosh function, BRexx, 364 date functions BRexx library for, 367 HHNS WorkBench, 278
d2b function, Rexx/imc, 350
countstr function
Db2 database, 248-249, 250-253
definition of, 90, 553 Open
Db2 SQL interfaces, 505
Object Rexx, 472 roo!, 454
Cowlishaw, Michael (Rexx inventor)
dbclose function, BRexx, 365
dbconnect function, BRexx, 365
The Rexx Language: A Practical Approach to
Programming (TRL-1), 193 DBCS (Double-Byte Character Set), 496-497
The Rexx Language (TRL-2), 8, 193, 310, 532 dberror function, BRexx, 365
role in Rexx language, 6, 308, 312 dbescstr function, BRexx, 365
CP assembler macros, 505 dbfield function, BRexx, 365
CP commands, VM Rexx, 506-511 DBForums Web site, 531
CP DIAGNOSE instructions, 505 dbget function, BRexx, 365
dbinfo function, BRexx, 365
CP system services, 505
dbisnull function, BRexx, 365
CPI (Common Programming Interface), 505
CPI Resource Recovery Routines, 505 DBMS (database management system). See database
CPICOMM interface, 505 dbsql function, BRexx, 365
crypt function, Regina, 337, 577 d2c function, 105, 556
CSL (Callable service library) routines, 505 DDL (Data Definition Language), 230
CSL function, VM Rexx, 498 debugging. See also error conditions; error trapping
CUR for Rexx, 616 interactive, 4, 6, 140-142
curses package, 111 methods of, 134
cursor processing, 245-247, 252 say instruction for, 133-135
Customer Information Control System (CICS), 323 trace function for, 139-140, 196, 197, 566-567
c2x function, 97, 105, 554 trace instruction for, 135-139, 140-142, 545-546
decimal (fixed-point) numbers, 26
dedicated devices, programming. See embedded
D programming
data conversions, of variables, 24 default environment, 211, 636
Data Definition Language (DDL), 230 delay function, HHNS WorkBench,277
Data Manipulation Language (DML), 230 DeleteFile function, Reginald, 391
data structures based on arrays, 63-64 delfile function, BRexx, 368
data type, of variables, 24, 25 delquery function, CGI/Rexx, 274
database. See also Rexx/SQL package DELSTACK command
binding variables, 247, 249-250 mainframe Rexx, 167
connections to, 233, 234-236, 248-250 OS/TSO Rexx, 502
cursor processing, 245-247, 252 delstack function, roo!, 454
information about, retrieving, 234-238
interfaces to, 203, 250-253
738
D-E
Index
delstr function, 90, 555
delword function, 92, 556
dense arrays, 53 DOS functions, BRexx library for, 367, 378-382
DESBUF command DOS platforms
CMS, 499 definition of, 8
mainframe Rexx, 166 return codes for OS commands, 154
desbuf function double quotes (“...”)
BRexx, 363 enclosing character strings, 23, 26
Regina, 167, 342, 577-578 enclosing OS commands, 181-182
Reginald, 396, 398 Double-Byte Character Set (DBCS), 496-497
deweb function, CGI/Rexx, 274 DO-WHILE construct, 34
DIAG function, VM Rexx, 498 Dr. Dialog interface, 257
DIAGRC function, VM Rexx, 498 DriveContext class, roo!, 455
Dialog Editor, Reginald, 390 DriveInfo function, Reginald, 391, 401
digits function, 196, 556 DriveMap function, Reginald, 391, 401
digits special name, NetRexx, 522, 629 driver, 42
dir function drop instruction, 115, 539
BRexx, 368 DROPBUF command
Reginald, 391 CMS, 499
directives, Open Object Rexx, 465-466, 470 mainframe Rexx, 166
Directory class, in ooRexx, 468, 619 OS/TSO Rexx, 502
directory function dropbuf function
Regina, 338, 578 BRexx, 363
Reginald, 391 Regina, 167, 342, 578
division operator (/), 27 Reginald, 396, 399
DLLs, Reginald, 395-396 roo!, 454
DML (Data Manipulation Language), 230 DW (Dynamic Windows), GUI package based on. See
DO construct, 34 Rexx/DW package
do forever instruction, 47-49 DW package, 111
do instruction d2x function, 105, 556
definition of, 37-38, 538 DYLD_LIBRARY_PATH variable, 340
example using, 38-40, 43-46 Dynamic Windows (DW), GUI package based on. See
NetRexx, 521, 630 Rexx/DW package
as structured construct, 34
subscripts for, 181
do over instruction, Reginald, 396-397
E
EBCDIC functions, BRexx library for, 367
do until instruction, 47-48
edit macros, 665-670
do while instruction, 37, 47-48 edit macros, VM Rexx, 496
document trees, 293, 294, 298 EditName function, in
documentation.
Reginald,391
See also publications
editors, Rexx-aware, 183
for BRexx interpreter, 360-1
Emitter class, roo!, 455
IBM Rexx manuals, 533
“Empirical Comparison of Seven Programming
for NetRexx interpreter, 517
Languages” (Prechelt), 11
for r4 interpreter, 447, 449
for Regina interpreter, 14, 333
for Reginald interpreter, 386, 387, 392-393
for Rexx/imc interpreter, 346
for roo! interpreter, 447, 449
do-end pair, 35-36, 37-38
739
E
encapsulation
Open Object Rexx,465 errors. See also debugging; error conditions
roo!, 452 insufficient storage error, 102
endless loop, 47 overflow error, 28, 102
end-of-file (EOF) character, 73, 74 underflow error, 28, 102
environment errortext function, 153,197,557
active, 211 event handlers, Rexx/DW, 264
default, 211, 636 event-driven scripts, 259, 265
determining, 195-199 example programs
other than OS, issuing commands to, 220 address instruction, 218-219
specifying for OS commands, 216-217 balanced parentheses, 118-120
.environment object, Open Object Rexx, 470 BRexx interpreter, Array I/O, 374-375
environmental functions BRexx interpreter, C-language I/O, 369-371
Regina, 338 BRexx interpreter, direct data access, 376-8
Rexx/imc, 348, 352-354 BRexx interpreter, DOS functions, 378-382
Rexx/SQL, 233 command procedures, 222-225
environmental variables, Open Object Rexx, 464 database information, retrieving, 234-239
EOF (end-of-file) character, 73, 74 database input, 85-89
eof function database tables, creating and loading, 239-241
BRexx, 365 database tables, selecting results from, 241-247
Regina, 339, 579 database tables, updating, 243-244
equals sign (=) four letter words, identifying, 38-40
assignment, 26 interpreter for user input, 146-151
equal comparison operator, 29 key folding, 106-107
equals sign, double (==), strictly equal operator, 29 mainframe Rexx, 506-511
ERROR condition,144,148 menu of transactions, 43-46
error conditions NetRexx interpreter, 525-526
list of, 144 NetRexx interpreter, squaring a number, 523-525
in Open Object Rexx, 472 Number Game (random numbers), 21-24
in OS TSO/E Rexx, 504 in Open Object Rexx, concurrency, 489-491
Reginald, 395 Open Object Rexx, file I/O, 476-477
in roo!, 454 Open Object Rexx, squaring a number, 477-479
untrapped, default actions for, 148 Open Object Rexx, stack implementation, 481-485
.error object, ooRexx, 470 Open Object Rexx, user interaction, 479-481
error trapping Open Object Rexx, video circulation, 485-489
affecting portability, 204 poetry scanner, 93-96
call instruction for, 144-146, 152-154 Reginald interpreter, file & drive management, 400-4
condition function for, 154, 155-156, 197, Reginald interpreter, MIDI files, 414-415
552-553 Reginald interpreter, speech recognition, 412-414
example using, 146-151 Reginald interpreter, Windows GUI, 404-412
generic routine for, 155-6 Reginald interpreter, Windows registry, 416-418
guidelines for, 143-146, 179-180 Rexx Server Pages (RSPs), 287-288
limitations of, 156 Rexx/gd library, 268-271
for OS commands, 213 Rexx/imc C-language I/O functions, 354-356
Reginald features for, 395 Rexx/imc environmental functions,352-354
signal instruction for, 144-6, 147-8, 152-4 Rexx/Tk package, 260-264
special variables for, 151 RexxXML library, 299-302
for SQL, 237-239 rightmost position of byte in string, 128-130
740
E-F
Index
script environment, 200-201
stack for interroutine communication, 165-166 external subroutine, 41
stack, using, 162-165 ExternalClass class, roo!, 455
telephone area code lookup, 61-62 externals function
weighted retrieval, 58-60 mainframe Rexx, 593
Exception class, roo!, 455 VM Rexx, 497
exception handling. See error trapping
exclamation (!), command identifier, 454
EXE Conversion Utility, r4 and roo! interpreters, 451 F
EXEC interface, 505 >F> in trace file, 139
EXECDROP command, CMS, 499 FAILURE condition, 144, 148
EXECIO command 657-664 .false object, Open Object Rexx, 471
CMS, 499, 508, 510 fdopen function, Rexx/imc, 351
definition of, 504 FIFO (first-in, first-out) queue, 160-161
OS/TSO Rexx, 502 file associations for Windows interpreters, 326-327
EXECLOAD command, 499 File class, roo!, 455
EXECMAP command, CMS, 499 file functions
EXECs, VM Rexx, 496 BRexx library for, 367
EXECSTAT command, CMS, 499 fileno function, Rexx/imc, 351
EXECUTIL command, 502 FileRexx Function Library, 616
files
exists function
appending to, 76
Regina, 339, 579
closing, explicit, 69, 71
roo!, 454
closing, implicit, 68
exit instruction
encryption/decryption of, Open Object Rexx, 473
definition of, 539
end-of-file (EOF) character, 73, 74
example using, 43, 46
opening, implicit, 68
NetRexx, 630
positions of, moving explicitly, 72
placement of, 113-114, 115, 186
as structured construct, 34 positions of, moving implicitly, 68
exp function redirecting command I/O to and from, 217
BRexx, 364 filesize function, HHNS WorkBench, 277
Rexx/imc, 349 FileSpec function, 391
expand function,Reginald,399 FileUt package, 616
finalize method, roo!,453
explicit concatenation, 80
exponential numbers, 25-26
find function
export function, Regina, 338, 579 mainframe Rexx, 594
expose instruction, Open Object Rexx, 471, 481 Regina, 337, 580
exposed variables, 124-128 VM Rexx, 497
Extensible Markup Language (XML), 291-292. See FINIS command, CMS, 499
also RexxXML library first-in, first-out (FIFO) queue, 160-161
Extensible Stylesheet Language Transformations fixed-point (decimal) numbers, 26
(XSLT), 292 floating point numbers, 26
extensions, 110, 111 flush function, BRexx, 365
external access functions, Reginald, 398 FolderContext class, roo!, 455
external data queue. See stack fork function, Regina, 338, 580
external functions form function, 196, 557
accessing from BRexx, 366 form special name, NetRexx, 522, 629
accessing from Regina, 339-342 FORMAT command, CMS, 510
definition of, 120 format function, 102-103, 557-558
external routines, 110
741
F-G
formatdate function, CGI/Rexx, 274
forums, 531-532
forward instruction, Open Object Rexx, 471 G
forward slash Gargiulo, Gabriel (REXX in TSO Environment), 513
/ (division operator), 27 GCS (Group Control System) interface, 505
// (remainder division operator), 27 gd package, 111
free-form (free-format) languages, 7, 23 gdImageColorAllocate function, 267, 271
freespace function, Regina, 338, gdImageCreate function, Rexx/gd, 271
580 gdImageDestroy function, Rexx/gd, 267, 271
French, Rexx forums, 532 gdImageFilledRectangle function, Rexx/gd, 267
FrmHdr function, HHNS WorkBench, 278 gdImageJpeg function, Rexx/gd, 267
FrmInp function, HHNS WorkBench, 278 gdImageLine function, Rexx/gd, 267
ftell function, Rexx/imc, 351, 356 gdImageRectangle function, Rexx/gd, 267
fullurl function, CGI/Rexx, 274 German, Rexx forums in, 532
The FUNCDEF feature, Reginald, 388 getch function, BRexx, 368
funcdef function, Reginald, 398 GetCookie function, HHNS WorkBench, 278
functions. See also call instruction; specific getcwd function, Rexx/imc, 348, 352
functions getenv function
arguments of, 23, 25 HHNS WorkBench, 277
bit string functions, 96-97 Regina, 338, 581
for BRexx, 363-366, 367-368 Reginald, 398
built-in functions, 110, 120 Rexx/imc, 348, 352
calling, 111-112, 184 getfullhost function, CGI/Rexx, 274
external functions, 120 getkwd function, HHNS WorkBench, 277
HHNS WorkBench, 278 GETMSG function, OS/TSO Rexx, 501
internal functions, 120 getowner function, CGI/Rexx, 274
list of, 547-571 getpid function
for mainframe Rexx, 593-595 HHNS WorkBench, 277
for Mod_Rexx, 282, 623-625 Regina, 338, 581
nesting, 111-112, 174-175 Reginald, 398
for numbers, 103-106 getspace function, Regina, 338, 581
for Open Object Rexx, 472 gettid function, Regina, 338, 582
passing parameters to, 116-117, 128, 185 GetTitle function, MIDI interface, 443
placement of, 113-115, 123, 177 getwparm function, HHNS WorkBench, 277
recursive, 121-123, 128-130 Gimp Toolkit, 256
for Regina, 335-339, 573-591 Glatt, Jeff (developer of Reginald), 312, 385
for Reginald, 387-392, 398-399 global variables, 127-128, 172, 178, 185
result of, 25, 111, 184-185 GLOBALV command, CMS, 499
Rexx/imc, 348-356 glue languages, 4, 9, 434
for Rexx/SQL, 233, 597-606 gmImagePng function, Rexx/gd, 267
for Rexx/Tk, 607-613 Goldberg, Gabe (The Rexx Handbook), 513
for RexxXML, 293-294 gotoxy function, BRexx, 368
for roo!, 448 Graphical Applications w/ Tcl and Tk (Johnson), 264
scope of variables in, 123-128 graphical user interface packages. See GUI packages
search order for, 120 greater than operator (>), 29
string functions, 89-90, 92-97 greater than or equal to operator (>=), 29 greater
syntax for, 23 than or less than operator (<> or ><), 29
user-defined functions, 110
for VM Rexx, 497-498
fuzz function, 196, 558
742
H-I
Index
Group Control System (GCS) interface, 505
HTML CGI-scripting functions, BRexx library for, 367
GTK+ package, 616
HTML (Hypertext Markup Language), 292
GTK toolkit, 256, 257
htmlbot function, CGI/Rexx, 274, 276
guard instruction, Open Object Rexx, 471
htmlbreak function, CGI/Rexx, 274
GUI packages. See also Windows GUI functions
HtmlGadgets package, 616
list of, 255-257
HtmlStrings package, 616
Rexx/DW package, 256-257, 264-266, 616
HTMLToolBar package, 616
Rexx/gd library, 266-271 htmltop function, CGI/Rexx,274
Rexx/Tk package, 256, 258-264, 607-613, 617
httab function, CGI/Rexx, 274
GUI trace panel, Reginald, 395
HX command, VM Rexx, 498
Hypertext Markup Language (HTML), 292
H
Hack package, 616
HackMaster, 435
I
IBM. history of Rexx and, 308-9
HALT condition, 144, 148
Rexx interpreters for, 321
Handheld PC Magazine, 430
IBM DB2 UDB Admin. Reference API, 252
handheld platforms. See also Android
IBM DB2 UDB Appl. Development Guide, 252
BRexx support for,435-440
IBM mainframe Rexx. See mainframe Rexx
definition of, 8, 10
IBM Object REXX interpreter, 257. See also Open
hash function, Regina,337, 582
Object Rexx interpreter
HE command, OS/TSO Rexx,502
IBM Rexx Family Web site, 533
help. See also documentation
IBM Rexx Language Web site, 533
for BRexx, 361
IDENTIFY command, CMS, 499, 508
for Reginald, 387, 390
if instruction
user groups, 531
definition of, 35-37, 539
for VM Rexx, 495
example using, 23, 38-40
Web forums, 531-532
NetRexx, 630
help command, example using, 222-225
as structured construct, 34
Henri Henault & Sons Web site, 277
if-else-if ladder, 36-37
The Hessling Editor (THE), 183, 665
IF-THEN construct, 34
Hessling, Mark (current developer for Regina), 312, 331
IF-THEN-ELSE construct, 34
hexadecimal numbers, 26
implicit concatenation, 80
HHNS WorkBench, 276-281, 616
import function
HI command
BRexx, 363
OS/TSO Rexx, 502
Regina, 338, 582
VM Rexx, 498
import instruction, NetRexx, 521, 631
high-level languages, 3
IMS (Information Management System) interface, 505
HT command
indentation, 172-173
OS/TSO Rexx, 502
index function
VM Rexx, 498
mainframe Rexx, 594
Regina, 337, 582-583
VM Rexx, 497
indexed strings, NetRexx, 521-522
Information Management System (IMS) interface, 505
initialize method, roo!, 453
inkey function, HHNS WorkBench, 277
743
I -J
InLineFile class, roo!, 455
InOutLineFile class, roo!, 455 for IBM, 321
input information from, 202
redirecting, 76 for Java environment, 320
standard, 68 list of, 12-13
input instructions. See parse instruction; pull location of, as first line of script, 204
instruction for mainframe platforms, 320
.input object, ooRexx, 470 multiple, on one computer, 325-327
input/output. See I/O for new Rexx programmers, 13 object-
insert function, 90, 558 oriented, 314, 319-320
install scripts, 209 standardization of, 8
instance creation operator (AA), 453 thread-safe, 281-288, 333
intr function,BRexx, 363
instance methods, Open Object Rexx, 466
instantiation, Open Object Rexx, 465, 478 I/O (input/output)
InStream class, roo!, 455 BRexx functions for, 364-366, 368-369
instructions. See also specific instructions character-oriented, 68, 72-75, 191
list of, 535-546 command I/O, controlling,216-219
multiple instructions on one line, 59 command I/O, stack for, 225-226, 342, 368-369
for NetRexx, 521, 630-633 conversational, 75
for Open Object Rexx, definition of, 67-69
471 operands of, 25 line-oriented, 68, 69-72, 191
for OS/TSO Rexx, 501 Open Object Rexx methods for, 476-477
for Reginald, 394, 396-397, 398 OS-specific features for, 76-77
for structured constructs, 34 portability of, 76-77, 205
for unstructured constructs, 47 redirected, 75-76, 213-215
for VM Rexx, 496-497 Regina functions for, 339, 342
insufficient storage error,102 Reginald functions for, 390-392
integer division operator (%), 27 Rexx/imc functions for, 351, 354-356
integers, 26. See also standard input, 68
numbers interactive standard output, 68, 75
debugging, 4 interview questions, 709-720
Interactive System Productivity Facility (ISPF), 183, iPhone, 445
505 IrDA communications, 441-442
ISPF edit macros, 665-670 ISAM package, 111
internal functions, 120. See also functions iSH, 445
internal routines, 110 ISPEXEC interface, 505
internal subroutines, 41, 110. See also subroutines ISPF (Interactive System Productivity Facility), 183,
international support, by Regina, 333 505,
Internet/REXX HHNS WorkBench, 276-281, 616 ISPF edit macros, 665-670
interpret instruction ISREDIT interface, 505
definition of, 540 iterate instruction
example using, 146, 439 definition of, 47, 50, 540
Reginald, 398 NetRexx, 631
interpreted languages, 4 iterateexe function, Reginald, 399
interpreters. See also compilers; tokenizers; specific
interpreters
affecting portability, 203 J
choosing, 13-14, 313-321 Java Native Interface, for roo!, 454
commercial, 322-323 Java, Rexx interpreter for. See NetRexx interpreter
for embedded programming, 321-322, 430 Java Runtime Environment (JRE), 517
free, 311-312 Java SDK (Software Development Toolkit), 517
for handheld platforms, 314, 321-322, 425 Jaxo. See Android
JCL for batch Rexx, 671-676
744
J-L
Index
Johnson, Eric F. (Graphical Applications w Tcl & Tk),
264 explicitly controlling file positions, 72
JRE (Java Runtime Environment), 517 return string for, 180, 196
justify function line-oriented I/O, 68, 69-72, 191
mainframe Rexx, 594 lineout function
Regina, 337, 583 closing file with, 71
Reginald, 399 definition of, 69, 559-60
Rexx/imc, 350 example using, 70
VM Rexx, 497 explicitly controlling file positions, 72
JSON, 689-694 return string for, 180, 196
lines function
745
L-M
LOSTDIGITS condition, 102, 144, 148, 472, 504
lower function methget function, CGI/Rexx, 274
Regina, 337 Method class, Open Object Rexx, 469, 620
roo!, 454 ::method directive, Open Object Rexx, 470
LU62 interface, 505 method instruction
NetRexx, 521, 631
roo!, 453
M method invocation infix operator (~), 453
macOS platforms, 8 method invocation operator (~), 465,466, 470
macro language, 111, 324-325 method invocation operator (~~), 466,470
macro programming, 10 method prefix operator (A),453
MacroEd package, 616 methods
mainframe platforms NetRexx, 520
definition of, 8 Open Object Rexx, 465, 466, 476, 619-622
interpreters for, 320 roo!, 452
stack implementation, 166-168 .methods object, Open Object Rexx, 471
emulation, 729-732 methpost function, CGI/Rexx, 274
mainframe Rexx MIDI I/O Function Library, 616
advantages of, 320 MIDI Rexx Function Library, 387, 414-415, 616
ANSI1-1996 and, 683-687 MIDICtlName function, MIDI Rexx, 415
Clist and, 677-681 MIDICtlNum function, MIDI Rexx, 415
definition of, 493-494 MIDIEventProp function, MIDI Rexx, 415
example using, 506-511 MIDIGetEvent function, MIDI Rexx, 415
extended functions for, 593-5 MIDIGetGMDrum function, MIDI Rexx, 415
interfaces to, 504-506 MIDIGetInfo function, MIDI Rexx, 415
migrating scripts to other platforms from, 512-513 MIDIGetVMPgm function, MIDI Rexx, 415
OS/TSO Rexx, 500-503 MIDINoteName function, MIDI Rexx, 415
platforms supported by, 493 MIDINoteNum function, MIDI Rexx, 415
standards supported by, 503-504 MIDIOpenFile function, MIDI Rexx, 415
VM Rexx, 494-499 MIDIPortName function, MIDI Rexx, 415
maintenance, 5, 6 MIDISaveFile function, MIDI Rexx, 415
MAKEBUF command MIDISetEvent function, MIDI Rexx, 415
CMS, 499 MIDISysex function, MIDI Rexx, 415
mainframe Rexx, 166 MIDITrack function, MIDI Rexx, 415
OS/TSO Rexx, 502 migration, using Rexx for, 10
makebuf function min function,103-4,561
BRexx, 363
minus sign (-)
Regina, 167, 342, 583
negative number prefix operator, 27
Reginald, 396, 399
subtraction operator, 27
roo!, 454
mkdir function, BRexx, 368
Map class, roo!, 455
mobile phones, 435-445. See also Android
MatchName function, Reginald, 391, 403-404, 411
platforms mod operator (remainder division operator)
Math class, roo!, 455
(//), 27
mathematical applications, using Rexx for, 11
Mod_Rexx interface
mathematical functions
definition of, 281-282, 616
BRexx, 364
example using, 287-288
HHNS WorkBench, 278
functions and variables in, 282, 623-627
Reginald, 387
installing, 282-283
Rexx/imc, 349
resources for, 288
max function, 103-104, 560-561
scripting with, 283-287
McPhee, Patrick T. J. (of RexxXML), 299
MenuObject class, Open Object Rexx, 622
Message class, Open Object Rexx, 469,620
746
M-O
Index
modularity newstack function, roo!, 454
definition of, 7, 33, 109
.nil object, Open Object Rexx, 470
example using, 118-120
guidelines for, 177-178 Nirmal, B. (REXX Tools and Techniques),513
Monitor class, Open Object Rexx, 469, 620 NOMETHOD condition, 472
movefile function nonsparse arrays (dense arrays), 53
BRexx, 368 nop instruction
Reginald, 391 definition of, 37, 541
MQ-Series Rexx interface, 505 NetRexx, 632
MSG function, OS/TSO Rexx, 501 NOSTRING condition, 472
msgbox function, BRexx, 368 not equal operator (\= or -=), 29
multiple inheritance, Open Object Rexx, 467 not greater than operator (\> or ->), 29
multiplication operator (*), 27 not less than operator (\< or -<), 29
MutableBuffer class, ooRexx, 469 “not” sign, ANSI-standard, 204, 496
MVS Forums, 532 MVS Help forum, 532 NOTREADY condition, 144, 148
MVSVAR function, OS/TSO Rexx,501 NOVALUE condition, 144, 148
MySQL database, 230, 249 null NetRexx special name,522,629
MySQL I/O functions, BRexx, 365-366 numbers
myurl function, CGI/Rexx, 274 calculation results identical across platforms, 27,
99
calculation rules for, 100-101
N conversion functions for, 105-6
NAMEFIND command, CMS, 508 definition of, 25-26, 100
nap function, roo!, 454 errors from calculations, 28, 102
Nash, Simon (developer of Object Rexx), 312 example using, 106-107
native programming, for handhelds, 435-445 exponential notation, 101-102, 103
natural language processing, 93-96 functions for, list of, 103-104
negative number prefix operator (-), 27 parsing by, 83-84
nesting functions,111-112, 174-175 significant digits (precision), 28, 101-102
NetRexx interpreter numeric comparisons, 28
advantages of, 317, 320, 515-516 numeric digits instruction,101-102
definition of, 13, 311, 312, 515 numeric form instruction, 102
documentation for, 517 numeric fuzz instruction, 101-102
downloading and installing, 517-518 numeric instruction
example using, 523-526 definition of, 28, 541
extra features in, 519-523 NetRexx, 632
instructions, 521, 630-633
Java knowledge required for, 516-517
Java software required for, 517 O
running scripts, methods for, 518-9 >O> in trace file, 139
special methods, 523, 630 Object class
special names, 522-523, 629 Open Object Rexx, 469, 621
Netview, 505 roo!, 455
Netware, Rexx for, 321 Object REXX GTK+ Project, 257
new method, Open Object Rexx, 465 Object REXX interpreter, 257, 310, 460. See also Open
newline character, 74 Object Rexx interpreter
NEWSTACK command ObjectCUR for Object REXX, 616
mainframe Rexx, 167 OBJECTION condition, 454
OS/TSO Rexx, 502 object-oriented interpreters, 314, 319-320. See also
NetRexx interpreter; Open Object Rexx interpreter;
r4 interpreter; roo! interpreter
747
O-P
object-oriented programming, learning, 475
objects, Open Object Rexx, 465, 470-1 OPENVM routines, 505
ODBC API (Open Database Connectivity Application operands, of instructions, 25
Programming Interface), 230, 232 operating system extensions, using Rexx for, 10
ODBC (Open Database Connectivity), 249 operating systems. See platforms
ODBC drivers, 387, 391, 616 operatorless condition test, 120
OLEObject class, ooRexx, 622 operators
OODialog, 257 arithmetic, 27-28
ooRexx. See Open Object Rexx interpreter comparison, 28-30
Open Database Connectivity Application Programming concatenation operator (||), 30-31, 80
Interface (ODBC API), 230, 232 logical, 30
Open Database Connectivity (ODBC), 249 object-oriented, in Open Object Rexx,469-470
Open Database Connectivity (ODBC) drivers, 387, 391, object-oriented, in roo!, 453-454
616 order of precedence for, 31-32
open function options instruction
BRexx, 365 definition of, 203, 334-335, 541-542
Regina, 339, 583-584 NetRexx, 521, 632
Rexx/imc, 351, 355 portability and, 205
Open Object Rexx interpreter (ooRexx) Reginald, 394
advantages of, 317, 319-320 VM Rexx, 496-497
built-in objects, 470-471 Oracle database, connecting to, 248
classes, 465-467, 468-469, 476, 619-622 oraenv function, CGI/Rexx, 274
definition of, 13, 310, 312, 459 order of precedence for operators, 31-32
directives, 470 OrderedVector class, roo!, 455
downloading and installing, 462-464 OS commands. See commands, OS
environmental variables for, 464 error OS platforms, 8
conditions, 472 OS simulation interface, 505
example using, concurrency, 489-491 OS/2 platforms, 8
example using, file I/O, 476-477 OS/2 Rexx
example using, squaring a number, 477-479 functions supported by Regina, 336
example using, stack implementation, 481-485 OS/400 platforms, 8
example using, user interaction, 479-481 example OS/TSO Rexx, 500-503
using, video circulation, 485-489 features of, 460-2 otherwise keyword, select instruction, 40-41
functions, 472 Ousterhout, John (“Scripting: Higher Level Programming
history of, 460 for the 21st Century”),11
instructions, 471 OutLineFile class, roo!,455
learning object-oriented programming with, 475 output
object-oriented features of, 464-468 redirecting, 76
operators, 469-470 standard, 68, 75
platforms supported by, 462, 472-473 output function, 73
Rexx API, 472 .output object, Open Object Rexx, 470
RexxUtil package, 472 OutStream class, roo!, 455
roo! interpreter as alternative to, 448 OUTTRAP function, OS/TSO Rexx, 501
special variables, 471 overflow error, 28, 102
Windows features, 472-473 overlay function, 90, 561
P
>P> in trace file, 139
package instruction, NetRexx, 521, 632
748
P
Index
packages for Rexx, list of, 615-618. See also
specific packages period (.), placeholder variable, 116
parameters persistent streams, 67-68
example using, 118-120, 128-130 Personal Rexx (Quercus Systems), 526
passing, Reginald features for, 396 PIPE command, CMS, 499
passing to script on command line, 115-116 placeholder variable (.), 116
passing to subroutines and functions, 116-117, 128, placeholder variable (?), 247, 252
185 platforms. See also portability; specific platforms
parent classes, Open Object Rexx, 466-467 OS-specific editors, 183
parentheses (()) OS-specific I/O features, 99 retrieving
affecting order of precedence, 31-2 information from, 202
enclosing function arguments,23 supported by BRexx interpreter, 359
parse arg instruction, 115-117, 185, 195 supported by mainframe Rexx, 493
parse instruction supported by Open Object Rexx, 462, 472-473
definition of, 26, 542 supported by r4 and roo!, 447
NetRexx, 632 supported by Regina, 14, 332
parsing by template, 82-85 supported by Reginald, 385
system information strings returned by, 635 supported by Rexx, 8-9, 309
VM Rexx, 496-497 supported by
parse linein instruction, 165 Rexx/imc, 345
parse pull instruction PlayASong function, MIDI interface, 443
affecting stack, 160-162, 164 plus sign
reading input, 39, 75 + (addition operator), 27
parse source instruction, 198, 352 + (positive number prefix operator), 27
parse value instruction, Rexx/imc, 350 +++ (in trace file), 139
parse version instruction, 198-199, 352 polymorphism
PARSECMD command, CMS, 499 NetRexx, 520
parsefid function, HHNS WorkBench, 277 Open Object Rexx,
467 roo!, 452
parsing, 79-80, 81-89, 701-704
path function, Reginald, 391 Poof! tool, r4 and roo! interpreters, 451
poolid function, Regina, 584
Pattern class, roo!,455
popen function
pattern matching, 80
pattern, parsing by, 82, 83 HHNS WorkBench, 277
PatternMatch class roo!, Regina, 338, 584
456 Reginald, 394
pclose function, Rexx/imc, 351 Rexx/imc, 351
percent sign (%), integer division operator, 27 portability. See also platforms
performance calculation results identical across platforms, 27,
of Apache Web server, 99
281 of BRexx, 359, 360 command procedures and, 210
of database interface, 230, 247 definition of, 190
of DOS emulation, 426-427 example using, 200-201
of I/O, portability and, 76-77 factors affecting, 190-192, 202-205
of Rexx/DW, 264 I/O and, 76-77, 205
of Rexx/Tk, 256
of scripting language, 5-6, 11
749
P
portability (continued)
migrating mainframe scripts to other platforms, public methods, Open Object Rexx, 466
512-513 publications. See also documentation; standards
OS commands and, 191-192, 201-202 “An Empirical Comparison of Seven Programming
Rexx for, 10 Languages” (Prechelt), 11
RexxUtil package for, 206-207 ANSI-1996, 8
script environment and, 195-199 Embedded Systems Programming Magazine, 430
standards and, 191, 192-195 Graphical Applications w Tcl & Tk (Johnson), 264
pos function, 87, 90-92, 561 Handheld PC Magazine, 430
positive number prefix operator (+), 27 IBM DB2 UDB Administrative Reference API, 252
pow function, BRexx, 364 IBM DB2 UDB Application Development Guide, 252
pow10 function, BRexx, 364 Learn REXX Programming in 56,479 Easy Steps, 386,
Prechelt, Lutz (“An Empirical Comparison of Seven 392
Programming Languages”), 11 Pocket PC Magazine, 430
precision. See numeric Programming Language Rexx Standard, 532
instruction prefix operators, 27 Programming with REXX Dialog, 392
preinitialize method, 453 Rexx: Advanced Techniques... (Kiesel), 513
printheader function, CGI/Rexx, 274, 276 The Rexx Handbook (Goldberg), 513
printvariables function, CGI/Rexx, 274, 276 REXX in the TSO Environment (Gargiulo), 513
private methods, Open Object Rexx, 466 The Rexx Language: Practical Approach to
procedure expose instruction, 124-128, 177 Programming (TRL-1) (Cowlishaw), 193
procedure hide instruction, Rexx/imc, 350 The Rexx Language (TRL-2), 8, 193, 310, 532
procedure instruction, 124-128, 543 REXX Tools and Techniques (Nirmal), 513
PROCESS construct, 34 REXX/VM Reference, 494, 593
program maintenance, 5, 6 REXX/VM User’s Guide, 511
Programming Language Rexx Standard, 532 RexxXML Usage and Reference, 299
programming style “Scripting: Higher Level Programming for the 21st
capitalization and, 170-171 Century” (Ousterhout), 11
code reviews, 183-184 Systems Application Architecture Common
comments, 175-177 Programming Reference, 494
common coding errors, avoiding, 184-187 Tcl/Tk in a Nutshell (Raines, Tranter), 264
error handling, 179-180 TSO/E REXX Reference, 500, 593
global variables, 178 TSO/E REXX User’s Guide, 511
methods of, 169-170 Using Mailslots, Reginald, 392
modularity, 177-178 Using Reginald to Access the Internet, 392
nesting, 174-175 Using Reginald w CG), 392
Rexx-aware editors, 183 Web sites listing, 531-2
site standards for, 183 The World of Scripting Languages (Barron), 11
structured code, 178-179 pull instruction
subscripts, 181 affecting stack, 160-162, 164
variable naming, 171-172 compared to parse pull instruction, 39
variables, declaring, 182 definition of, 23, 26, 81, 543
white space (spacing and indentation), 172-173 push instruction, 160-162, 163, 543
Programming with REXX Dialog, 392 putenv function, Rexx/imc, 348
PROMPT function, OS/TSO Rexx, 501 Python <--> Rexx, 725-728
properties instruction, NetRexx, 521, 632
properties, NetRexx, 520
protect instruction, NetRexx, 521
prototyping, using Rexx for, 10
750
Index
Q-R
Q random numbers, example program using, 21-24
QBUF command
mainframe Rexx, 166 randu function, Regina, 338, 585
OS/TSO Rexx, 502 rc variable, 147, 151, 197, 211, 213
QELEM command, OS/TSO Rexx, 502 read function, BRexx, 365
QSTACK command read position, 68
mainframe Rexx, 167 readch function, Regina, 339, 585
OS/TSO Rexx, 502 readform function, CGI/Rexx, 274, 276
qualify function readln function, Regina,339, 585
definition of, 562 readpost function, CGI/Rexx,274
Reginald, 391 real numbers, 26
Quercus Systems, Personal Rexx, 526 real-time operating system (RTOS), 430
QUERY command, CMS, 499, 508 record-oriented files, 86
querymacro function, Reginald, 398 recursion, 121-123, 128-130
question mark (?), placeholder variable, 247, 252 redirected I/O, 75-76, 213-215
questions at end of chapter, answers for, 637-655 Regina interpreter
queue. See stack advantages of, 332-333
Queue class benefits of, 13-14
Open Object Rexx, 468, 620 bit manipulation functions, 336-337
roo!, 456 command I/O, stack for, 342
queue instruction, 160-162, 163-164, 543-544 definition of, 12, 312, 317-318
queued function documentation for, 14, 333
definition of, 160, 163, 164, 562 downloading, 14-15
Reginald, 399 environmental functions, 338
quotes, double (“...”) example using, 343
enclosing character strings, 23, 26 extended functions for, 335-339, 573-591
enclosing OS commands, 181-182 external function libraries, accessing, 339-342
quotes, single (‘...’), enclosing character strings, 23, 26 extra features in, 316, 334
for handheld platforms, 425
history of, 331
R installing, 15-19
r4 interpreter international support, 333
advantages of, 316, 319, 447-448 interpreter options for, 334-5
definition of, 13, 312, 447 I/O functions, 339
documentation for, 447, 449 open source, 333
downloading and installing, 448-449 platforms supported by, 14,332
support for, 448 Rexx API support, 333
tools for, 450-451 SAA API, 343
Windows GUI functions, 448 stack functions, 342
Raines, Paul (Tcl/Tk in a Nutshell), 264 standards supported by, 332
raise instruction string manipulation functions, 337
Open Object Rexx, 471, 472 supercompatibility with other interpreters, 333
Reginald, 395 superstacks, 333 support community for, 332
raise to a power operator (**), 27 thread-safe, 333
raiseObjection function, 454 Regina Rexx language project, 531
random function Reginald interpreter
definition of, 23, 25, 103-104, 562 Administration tool, 387, 399, 615
Reginald, 399 advantages of, 316, 318, 385-386
751
R
Reginald interpreter (continued)
array indexing, 397 ::requires directive, Open Object Rexx, 470
definition of, 13, 312, 385 resources. See also publications; Web sites
do over instruction, 396-397 standards for Rexx, 8, 191, 192-195, 532
documentation for, 386, 387, 392-393 user groups, 531
downloading and installing, 386 Web forums, 531-532
error conditions, defining and raising, 395 response handlers, 284
example using, file and drive management, 400-404 REstructured eXtended eXecutor. See Rexx language
example using, MIDI files, 414-415 result variable
example using, speech recognition, 412-414 definition of, 41, 110, 111, 151, 197
example using, Windows GUI, 404-412 example using, 112-113
example using, Windows registry, 416-418 signal instruction and, 152
extended functions, 387-388 return code. See rc variable
external access functions, 398 return instruction
GUI trace panel, 395 definition of, 41, 43, 110, 115, 544
interpret instruction, 398 nesting, 111-112
I/O functions, 390-392 NetRexx, 632
MIDI Rexx function library, 387, 414-415, 616 placement of, 186
miscellaneous functions, 399 signal instruction and, 152
ODBC drivers, 391 as structured construct, 34
options instruction, 394 reverse function, 90, 123, 562
OS commands, issuing, 393-394 Revu tool, r4 and roo! interpreters, 451
parameter passing, 396 REXREF3 package, 616
platforms supported by, 385 REXX. See mainframe Rexx
REXX Dialog, 387, 388-390, 404-409 Rexx 2 Exe utility, 387, 616
REXX Dialog IDE, 390, 400 Rexx: Advanced Techniques... (Kiesel), 513
Rexx Text Editor (RexxEd), 183, 398, 399 Rexx API, 324, 333, 472
Script Launcher, 399, 618 REXX Dialog IDE (RxDlgIDE), 390, 400
scripting with, 399-400 Rexx Dialog package, 257, 616
sorting array items, 396 REXX Dialog, Reginald, 387, 388-390, 404-409
speech function library, 412-4 Rexx Exits interface, 505
SQLite driver, 387, 392 Rexx for CICS, 320
stack functions, 396, 398-399 The Rexx Handbook (Goldberg), 513
standards supported by, 386 Rexx home page, 532
system information functions, 398 REXX in the TSO Environment (Gargiulo), 513
tools for, 386-387 Rexx interpreters. See interpreters
Windows DLLs, 395-396 Rexx LA (Rexx Language Association), 460, 531
Windows GUI functions (REXX Dialog), 388-90, 404-12
Windows registry, accessing, 395, 416-418
Reginald Rexx Forum, 532
regular expressions library, Reginald, 388
Regular Expressions package, 616
RegUtil package, 617
Relation class, Open Object Rexx, 468, 620
relational database, 229. See also database
remainder division operator (//), 27
reply instruction, Open Object Rexx, 471
request record pointer, 286
752
R
Index
Rexx language
Rexx/ISAM package, 617
benefits of, 6-7
Rexxlets, 434
elements of, 24-26
RexxMail package, 617
features of, 7-8
RexxRE package, 617
free implementations of, 310, 311-312
rexxSMLFini function, RexxXML, 294
future of, 327
Rexx/SQL package
history of, 6, 189-190, 192-193, 308-311
alternatives to, 250-253
interpreter for, choosing, 13-14, 313-321
binding variables, 247, 249-250
limitations of, 11
cursor processing, 245-247
packages and tools supported by, 615-618
database connections, 233, 234-236, 248-250
platforms supported by, 8-9, 309
databases supported by, 229-230, 248-250
standards for, 8
definition of, 111, 617
uses of, 9-11, 12
downloading, 231-232
The Rexx Language: A Practical Approach to
environmental control, 233
Programming (TRL-1) (Cowlishaw), 193
environmental functions, 233
Rexx Language Association (Rexx LA), 460
error trapping in, 237-239
The Rexx Language (TRL-2) 8, 193, 310, 532
features of, 230
Rexx Math Bumper Pack, 616
functions in, list of, 233, 597-606
Rexx newsgroup forum, 531
installing, 232
Rexx Server Pages (RSPs), 287-288
issuing SQL statements, 233, 250
Rexx Sockets, 505
tables, creating and loading, 239-241
Rexx Speech library, 412-414
tables, selecting results from, 241-244, 245-247
Rexx Text Editor (RexxEd), 183, 387, 399, 617
tables, updating, 243-244
REXX Tools and Techniques (Nirmal), 513
transactions, 233
Rexx2Nrx package, 617
RexxTags package,617
Rexx-aware editors, 183
Rexx/Tk package
Rexx/CGI library (cgi-lib.rxx), 274-276, 615
definition of, 256, 258, 617
Rexx/CURL package, 616
downloading and installing, 258-259
Rexx/Curses package, 616
example using, 260-264
Rexx/DB2 package, 250-253
functions in, list of, 607-613
Rexx/DW package, 256-257, 264-266, 616
resources for, 264
RexxEd (Rexx Text Editor), 183, 387, 399, 617
scripting with, 259, 264
Rexx/gd library, 266-271, 617
Rexx/Trans package, 617
Rexx/imc interpreter
RexxUtil package, 206-207, 387, 472, 617
advantages of, 316, 318, 345-346
REXX/VM Reference, 494,593
C-language I/O functions, 351, 354-356
REXX/VM User’s Guide, 511
definition of, 12, 312, 345
Rexx/Wrapper package, 617
documentation for, 346
RexxXML library
environmental functions, 348, 352-354 applying stylesheet to document, 299
example using, 352-356 definition of, 111, 292, 617
extra features in, 348 downloading and installing, 295
351 installing, 346-348 example using, 299-302
miscellaneous functions, 350 features of, 292
packages and tools supported by, 351-352 functions for, list of, 293-294
platforms supported by, 345 licensing, 295
SAA interface functions, 350 loading, 296
stack functions, 350
processing XML documents, 296-297, 298 updating
standards supported by, 345
XML documents, 297
transcendental mathematical functions, 349
validating documents against schemas, 298
RexxXML Usage and Reference, 299
753
R-S
rexxXMLInit function, RexxXML, 294
right function,90,122,563 rxfuncquery function
rmdir function, BRexx, 368 Regina, 586-587
roo! interpreter Reginald, 398
advantages of, 317, 319, 447-448 SAA, 341, 350
definition of, 13, 310, 312, 447 RxInfo function, REXX Dialog,389
documentation for, 447, 449 rxJava package, 617
downloading and installing, 448-449 RxMakeShortcut function, REXX Dialog, 389
object-oriented programming with, 452-456 RxMsg function, REXX Dialog, 389, 408
support for, 448 RxProject package, 618
tools for, 450-451 RxQuery function, REXX Dialog, 389
Windows GUI functions, 448 rxqueue executable, Regina, 342
::routine directive, ooRexx, 470 rxqueue function
routines. See functions; subroutines Regina, 168, 338, 342, 587
.rs object, Open Object Rexx, 471 Reginald, 396, 399
r4Sh function, HHNS WorkBench, 278 RxRSync package, 618
RSPs (Rexx Server Pages), 287-288 RxRunRamScript function, REXX Dialog,389
RT command RxRunScript function, REXX Dialog, 389
OS/TSO Rexx, 502 RxSay function, REXX Dialog, 408
VM Rexx, 498 RxSet function, REXX Dialog,389
RTOS (real-time operating system), 430 RxSock package, 111, 388, 618
RxAcc package, 617 rxstack executable, Regina,342
RxBlowFish package, 617 RxWav package, 618
RxCalibur package, 617
RxComm Serial Add-on package, 388, 617
RxCreate function, REXX Dialog, 389, 407
S
RXDDE package, 617 SAA API
RxDlgDropFuncs function, REXX Dialog, 389 definition of, 324
RxDlgIDE package, 617 Regina, 336, 343
RxDlgIDE (REXX Dialog IDE), 390, 400
Reginald, 386
RxDlgLoadFuncs function, REXX Dialog, 389 Rexx/imc, 350
SAA (Systems Application Architecture) standard, 193,
RxErr function, REXX Dialog, 389, 407
309, 532
RxFile function, REXX Dialog, 389
rxfuncadd function say instruction
debugging with, 133-135
Regina, 585-586
default environment, determining, 636
Reginald, 398
definition of, 45, 75, 544
REXX Dialog, 389
NetRexx, 633
Rexx/DW, 265
system information strings returned by, 635
Rexx/gd, 267
sayn instruction, Rexx/imc, 350
Rexx/Tk, 261
scheduled tasks, 210
SAA, 341, 350
schema validation, with RexxXML, 294, 298
rxfuncdrop function
scoping, 123-130
Regina, 586
screen interfaces, affecting portability, 203
Reginald, 398
Script Launcher, 399, 618
SAA, 341, 350
“Scripting: Higher Level Programming for the 21st Cen
rxfuncerrmsg function
tury” (Ousterhout), 11
Regina, 586
scripting language, 311-315
Reginald, 398
definition of, 3-4
SAA, 341
performance of, 5-6, 11
Rexx as, 308
754
S
Index
SearchPath function, Reginald, 391
sleep function
seek function
Regina, 338, 588
BRexx, 365
Reginald, 399
Regina, 339, 587
smart phones See Android
select instruction
Socket class, roo!, 456
as CASE construct, 34, 37 SOCKET function, VM Rexx, 498
definition of, 40-41, 544-545
SORT command, CMS, 510
example using, 43-46
Sort statement, Reginald, 396
NetRexx, 521, 633
source special name, NetRexx, 522, 629
Rexx/imc, 350
sourceline function, 150, 153, 196, 197, 563
self reference, roo!, 453
space function, 92, 563-564
self variable, Open Object Rexx, 471
spacing and indentation, 172-173
semicolon (;), line separation character, 59, 173
sparse arrays, 53
SENTRIES command, CMS, 499
special characters, I/O and, 74-75
Set class
special methods, NetRexx, 523, 630
Open Object Rexx, 468, 620
special names, NetRexx, 522-523, 629
roo!, 456
special variables
SET command, CMS, 499
for Mod_Rexx, 626-627
SETLANG function, OS/TSO Rexx, 501
for Open Object Rexx,471
SHARE users group, 531
rc variable, 147, 151, 197, 211, 213
shared variable, roo!, 453
result variable, 41, 151, 197
shell language scripts, running Rexx as, 326
sigl variable, 151, 197
shell scripts. See command procedures
Speech Function Library, 387, 412-414, 618
SHLIB_PATH variable, 340
SpeechClose function, Rexx Speech, 414
show function, Regina, 338, 587-588 SpeechOpen function, Rexx Speech, 413
sigl variable, 151, 197
SpeechPitch function, Rexx Speech, 414
sign function, 103-104, 563 SpeechSpeak function, Rexx Speech, 413
signal instruction SpeechSpeed function, Rexx Speech, 414
compared to call instruction, 152-154 SpeechVoiceDlg function, Rexx Speech, 414
definition of, 47, 49-50, 545 SpeechVolume function, Rexx Speech, 414
error trapping with, 144-146, 147-148 split function, roo!, 454
mainframe Rexx, 504
SQL Communications Area (SQLCA), 230, 238 SQL
NetRexx, 633
statements. See also Rexx/SQL package
signal off instruction, 148
binding variables, 247, 249-250
signal on instruction, 148
issuing, 233, 250, 251
signatures, NetRexx, 520
support for, 230
significant digits. See numeric instruction
tables, creating and loading, 239-241
simple symbols (variable names), 25, 54, 171-2
tables, selecting results from, 241-244, 245-247
simple variable name, 25 tables, updating, 243-244
sin function SQLCA (SQL Communications Area), 230, 238
BRexx, 364 SqlClose function, Rexx/SQL, 233, 245, 597
Rexx/imc, 349 SqlCommand function, Rexx/SQL, 233, 597-598
single board computers, 421-433
SqlCommit function, Rexx/SQL, 233, 598
single quotes (‘...’), enclosing character strings, 23, 26
SqlConnect function, Rexx/SQL, 233, 598-599
sinh function, BRexx, 364
SqlDefault function, Rexx/SQL, 233, 599
skewed tree, 63
SqlDescribe function, Rexx/SQL, 233, 245, 599-600
SLAC (Stanford Linear Accelerator Laboratory), 274
SqlDisconnect function, Rexx/SQL, 233, 600
slacfnok function, CGI/Rexx, 275
SqlDispose function, Rexx/SQL, 233, 601
slash
SqlDropFuncs function, Rexx/SQL, 601
/ (division operator), 27
// (remainder division operator), 27
755
S
SqlExecute function, Rexx/SQL, 233, 601
SqlFetch function, Rexx/SQL, 233, 245, 602 Stem class, Open Object Rexx, 469, 621
SqiGetData function, Rexx/SQL, 233, 602-603 stem variables (array names), 55
SqlGetInfo function, Rexx/SQL, 233, 603 stemdelete function, Reginald, 399
SQLite driver, Reginald, 387, 392, steminsert function, Reginald, 399
SQLite for BRexx, 371-373 storage function
SqlLoadFuncs function, Rexx/SQL, 603-604 BRexx, 363
SqlOpen function, Rexx/SQL, 233, 245, 604 OS/TSO Rexx, 501
SqlPrepare function, Rexx/SQL, 233, 245, 604 Regina, 338, 589
SqlRollback function, Rexx/SQL, 233, 605 VM Rexx, 498
SqlVariable function, Rexx/SQL, 233, 605-606 Stream class, Open Object Rexx, 469, 621
sqrt function stream function
BRexx, 364 BRexx, 365
Rexx/imc, 349 definition of, 71-72, 196, 564
squareRoot function, roo!, 454 portability and, 205
S/Rexx (Treehouse Software Inc.), 323 Regina, 339, 589
stack Rexx/imc, 351
affecting portability, 205 VM Rexx, 497
BRexx functions for, 363 stream instance, 477
buffers and, 167-168 streams, I/O, 67-68, 216, 217
for command I/O, 225-226, 342 strict comparison operators, 29-30, 187
definition of, 159-162 strictly equal operator (==), 29
example using, 162-166 strictly greater than operator (>), 29
instructions affecting, 160 strictly greater than or equal to operator (>=), 29
maximum size of, 162 multiple, strictly less than operator (<<), 29
in Reginald, 396 number of strictly less than or equal to operator (<<=),29
items in, 160 strictly not equal operator (\== or -==), 29
strictly not greater than operator (\> or ->),
object-oriented, in Open Object Rexx, 481-5
29
portability of, 166-168
strictly not less than operator (\<< or -<<),
Regina functions for, 342
29
Reginald functions for, 396, 398
String class, Open Object Rexx, 469, 621
399 Rexx/imc functions for, 350
string comparisons,28
superstacks, in Regina, 333
string delimiters, 26
Stack class, 456
string manipulation, 79-80, 337
standard input, 68
string processing, 79
standard output, 68, 75
strings
standards
bit string functions, 96-97
history of, 192-195
concatenating, 79-80
list of, 8, 532
example using, 85-89, 93-96
for mainframe Rexx, 503-504
functions for, list of, 89-90
portability and, 191, 195
in literals, 23
Standord Linear Accelerator Laboratory (SLAC), 274
parsing, 79-80, 81-85
state function
word-oriented functions for, 92-96
Regina, 338, 588 strip function, 87-88, 90, 564
Reginald, 391 striphtml function, CGI/Rexx, 275
statements. See instructions structured programming, 7, 33-34, 178-179
static variable, roo!, 453 study question answers, 637-655
.stderr object, Open Object Rexx, 471 style. See programming style
.stdin object, Open Object Rexx, 471 stylesheets. See XSLT (Extensible Stylesheet Language
.stdout object, Open Object Rexx, 471 Transformations)
subclasses, Open Object Rexx, 466-467
756
S-T
Index
SUBCOM command, OS/TSO Rexx, 502
subroutines. See also call instruction
calling, 112 T
definition of, 41 Table class
error handling using, 144-146 Open Object Rexx, 468, 620
example using, 43-46 roo!, 456
label for, 42-43 tables (arrays). See arrays
passing parameters to, 116-117, 128, 185 tables (database)
placement of, 113-115, 123, 177 creating and loading, 239-241
recursive, 121-123 selecting results from, 241-244, 245-247
result of, 112-113 updating, 243-244
scope of variables in, 123-128 Tags function, HHNS WorkBench,278
types of, 41 tail of compound symbol, 55
subscripts, 181 Talkabout Network, 531
substr function, 88-89, 90, 565 tan function
subtraction operator (-), 27 BRexx, 364
subword function, 93, 565 Rexx/imc, 349
super special method, NetRexx, 523, 630 tanh function, BRexx, 364
super special name, NetRexx, 522, 629 TblHdr function, HHNS WorkBench, 278
super variable, Open Object Rexx, 471 Tcl Developer Exchange Web site, 258
superclasses, Open Object Rexx, 466-467 Tcl/Tk in a Nutshell (Raines, Tranter), 264
superstacks, Regina support for, 333 Tcl/Tk scripting language, GUI package using. See
Supplier class, Open Object Rexx, 469, 621 Rexx/Tk package
suspect function, CGI/Rexx, 275 TE command
OS/TSO Rexx, 502
symbol function, 112, 565
VM Rexx, 498
symbolic pointers, 63 symbols.
Tek-Tips Rexx Forum, 532
See also labels
template, parsing by, 82-85
compound (array names), 54-55
testing. See debugging
simple (variable names), 25, 54
text processing, 10. See also string manipulation;
SYNTAX condition, 144, 148
strings
SYSCPUS function, OS/TSO Rexx,501
textual analysis, 93-96
SYSDSN function, OS/TSO Rexx,501
THE (The Hessling Editor), 183, 618
System class, roo!, 456
this special method, NetRexx, 523, 630
system function, Rexx/imc, 348, 352
this special name, NetRexx, 523, 629
system functions
thread-safe interpreter, 281-288, 333
BRexx, 363
tilde, double (~~), method invocation, 466-70
Reginald, 398
Rexx/imc, 348, 352 tilde (~), method invocation, 453, 465-6, 470
system information strings, 635 time function
SystemPropertyMap class, roo!, 456 definition of, 196, 566
systems administration, using Rexx for, 10 mainframe Rexx, 504
Systems Application Architecture Common tiny Linux, 430
Programming Reference, 494 Tk, GUI package using. See Rexx/Tk package TK
Systems Application Architecture (SAA) standard, package, 111
193, 309, 532 TkActivate function, Rexx/Tk, 607
systems programming languages, 11 TkAdd function, Rexx/Tk, 262-263, 607
SYSVAR function, OS/TSO Rexx, 501
757
T
TkAfter function, Rexx/Tk, 607
TkBbox function, Rexx/Tk, 607 TkText function, Rexx/Tk, 610
TkButton function, Rexx/Tk, 607 TkTextTagBind function, Rexx/Tk, 611
TkCanvas functions, Rexx/Tk, 607-608 TkTextTagConfig function, Rexx/Tk, 611
TkCget function, Rexx/Tk, 608 TkTopLevel function, Rexx/Tk, 611
TkCheckButton function, Rexx/Tk,608 TkTree functions, Rexx/Tk, 611-612
TkChooseColor function, Rexx/Tk,608 TkVar function, Rexx/Tk, 611
TkChooseDirectory function, Rexx/Tk, 608 TkVariable function, Rexx/Tk, 611
TkCombobox functions, Rexx/Tk, 612 TkWait function, Rexx/Tk, 611
TkConfig function, Rexx/Tk, 608 TkWinfo function, Rexx/Tk, 611
TkCurSelection function, Rexx/Tk, 608 TkWm function, Rexx/Tk, 611
TkDelete function, Rexx/Tk, 608 TkXView function, Rexx/Tk, 611
TkDestroy function, Rexx/Tk, 608 TkYView function, Rexx/Tk, 611
TkDropFuncs function, Rexx/Tk, 263, 611 tokenized scripts, Open Object Rexx, 473
TkEntry function, Rexx/Tk, 608 tokenizers, 324
TkError function, Rexx/Tk, 258, 608 tools, list of, 615-618
TkEvent function, Rexx/Tk, 608 TopHat tools, r4 and roo! interpreters, 451
TkFocus function, Rexx/Tk, 608 topower function, Rexx/imc, 349
TkFont functions, Rexx/Tk, 608-609 trace function, 139-140, 196, 197, 566-567
TkFrame function, Rexx/Tk, 609 trace instruction
TkGet function, Rexx/Tk, 609 definition of, 135-139, 140-142, 545-546
TkGetOpenFile function, Rexx/Tk, 263, 609 NetRexx, 521, 633
TkGetSaveFile function, Rexx/Tk, 609 trace panel, Reginald, 395
TkGrab function, Rexx/Tk, 609 trace special name, NetRexx, 523, 629
TkGrid functions, Rexx/Tk, 609 trailing comments, 22
TkImageBitmap function, Rexx/Tk, 609 transactions, Rexx/SQL, 233 transcendental
TkImagePhoto function, Rexx/Tk, 609 mathematical functions. See
TkIndex function, Rexx/Tk, 609 mathematical functions
TkInsert function, Rexx/Tk, 609 transient streams, 67
TkItemConfig function, Rexx/Tk, 609 translate function, 90-92, 95, 567
TkLabel function, Rexx/Tk, 609 Tranter,Jeff (Tcl/Tk in a Nutshell), 264
TkListbox function, Rexx/Tk, 609 Tree class, roo!, 456
TkLoadFuncs function, Rexx/Tk, 261, 611 Treehouse Soft. S/Rexx, 323, 63-4
TkLower function, Rexx/Tk, 610 trim function, Regina, 337, 589
TkMCListbox functions, Rexx/Tk, 612-613 TRL-1 standard, 193
TkMenu functions, Rexx/Tk, 610 TRL-1 (The Rexx Language: A Practical Approach to
TkMessageBox function, Rexx/Tk, 263, 610 Programming) (Cowlishaw), 193
TkNearest function, Rexx/Tk, 610 TRL-2 standard, 193-195
TkPack function, Rexx/Tk, 610 TRL-2 (The Rexx Language), 8, 193, 310, 532
TkPopup function, Rexx/Tk, 610 .true object, Open Object Rexx, 471
TkRadioButton function, Rexx/Tk,610 trunc function, 103-104, 567-568
TkRaise function, Rexx/Tk, 610 TS command
TkScale function, Rexx/Tk, 610 OS/TSO Rexx, 502
TkScan function, Rexx/Tk, 610 VM Rexx, 498
TkScrollbar function, Rexx/Tk, 610 TSO/E Rexx, 500-503
TkSee function, Rexx/Tk, 610 TSO/E REXX Reference, 500,593
TkSelection function, Rexx/Tk, 610 TSO/E REXX User’s Guide, 511
TkSet function, Rexx/Tk, 610 typeless variables, 4, 25
TkSetFileType function, Rexx/Tk,610
TkTcl function, Rexx/Tk, 610
758
Index
U -W
U ValueOut function, Reginald, 391
u2a function, BRexx, 368 vardump function, BRexx, 363
variable management, 4
uname function variable names (simple symbols), 25, 54, 171-172
Regina, 338, 590
variables
Reginald, 398
assigning, 25, 26
unbalanced tree, 63
binding, for SQL, 247, 249-250
underflow error, 28, 102
data type of, 23, 25
underscore (_), in variable names, 171
declaration of, 23, 25, 172, 182
Unicode support, Open Object Rexx, 473 definition of, 25
Uniform Resource Identifier (URI), 436 exposed, 124-128
uninitialized variables, 26 global, 127-128
uni-Rexx (The Workstation Group), 275, 323
placeholder variable (.),116
universal languages, 8-9 scope of, 123-128
Unix platforms, 8
typeless, 4, 25
unixerror function uninitialized, 26
Regina, 338, 590 uninitializing, 115
Reginald, 398 variables (attributes), Open Object Rexx, 465
unstructured programming, 47-50, 179 vector class reference operator ({}), 454
upper camel case, 171
Vector class, roo!, 456
upper function
verify function, 88, 90-92, 568
Regina, 337, 590-591
version special name, NetRexx, 523, 629
roo!, 454
vertical applications, r4 and roo! interpreters, 451
upper instruction, VM Rexx, 496-7
vertical bar, double (||), concatenation operator,
uppercase. See case sensitivity
30-31, 80
URI (Uniform Resource Identifier), 436
vertical bar (|), logical OR operator, 30
URLs, retrieving data from, with RexxXML, 294
VisPro Rexx interface, 257
Use Arg function, Reginald, 396
Vlachoudis, Vasilis (inventor of BRexx), 312, 359
use arg instruction, ooRexx, 466
VM GUI interface, 505
use instruction, ooRexx, 471 VM platforms, 8
USER condition, 472 VM Rexx
user groups, 531 ANSI-standard “not” sign,496
user-defined functions, 110 CMS commands, 499
userid function CMS immediate commands, 498
mainframe Rexx, 595 comment on first line of script,494
Regina, 338, 591 compilers, 498-499
Reginald, 398 enabling buffer functions for,573
Rexx/imc, 348, 352 file types, 496
VM Rexx, 497 functions, 497-498
Using Mailslots with Reginald, 392 instructions, 496-497
Using Reginald to Access the Internet, 392 online help, 495
Using Reginald with a Common Gateway Interface OS commands, 496
(CGI), 392 VSAMIO interface, 505
VSE platforms, 8
759
W
Web forums, 531-532
Web servers, programming webify function, CGI/Rexx,275
Apache Web server, 281-288 Wegina package, 618
with CGI, 273-281 wherex function, BRexx,368
methods for, 273 wherey function, BRexx,368
Web sites white space, 23, 172-173
Amiga Forum, 532 whole numbers (integers),26
AROS, 323 WideCharacterVector class, roo!, 456
Bochs emulator, 424 widgets, Rexx/DW, 256, 264
BRexx interpreter, 360-361 WindowObject class, Open Object Rexx, 622
Code Comments community,531 Windows CE platforms.
DBForums, 531 BRexx functions for, 363-364
embedded programming, 430 Windows DLLs, Reginald, 395-396
handheld devices, 430 Windows GUI functions. See also GUI packages
Henri Henault & Sons Web site, 277 r4 and roo! interpreters, 448
IBM DeveloperWorks, 288 Reginald, 388-390, 404-412
IBM Rexx Family, 533 IBM Windows Internet API, Reginald, 388
Rexx Language, 533 IBM Windows platforms
Rexx manuals, 533 definition of, 8
Mod_Rexx interface, 282, 288 installing Regina interpreter on, 15-16
MVS Forums, 532 MVS I/O redirection on, 76
Help forum, 532 multiple interpreters running on, 326-327
NetRexx interpreter, 517 Open Object Rexx classes for, 621-622
Open Object Rexx interpreter, 460, 462 Open Object Rexx support for, 472-473
PocketConsole emulator, 424 Reginald support for, 385-386
Quercus Systems, 323 return codes for OS commands, 154
r4 interpreter, 448 Windows registry, accessing with Reginald, 395, 416-8
Regina interpreter, 14 WindowsClipBoard class, ooRexx, 622
Regina Rexx language project, 531 WindowsEventLog class, ooRexx, 622
Reginald interpreter, 386 WindowsManager class, ooRexx, 622
Reginald Rexx Forum, 532 WindowsProgramManager class, ooRexx, 621
Rexx home page, 532 WindowsRegistry class, ooRexx, 621
Rexx LA, 531 windowtitle function, BRexx, 368
Rexx/DW package, 257, 265 word function, 93, 569
Rexx/gd library, 267 wordindex function, 93, 569
Rexx/imc interpreter, 346, 352 wordlength function, 93, 95, 569
Rexx/Tk package, 256, 258 word-oriented functions, 92-96
roo! interpreter, 448 wordpos function,93, 95, 569
SHARE users group,531 words
SLAC (Stanford Linear Accelerator Laboratory), 274 definition of, 92
SQLite, 392 parsing by, 82-83
Talkabout Network, 531 words function, 93, 95, 570
Tek-Tips Rexx Forum, 532 The Workstation Group, uni-Rexx, 326
Treehouse Software Inc., The World of Scripting Languages (Barron), 11
323 WAVV Forum, 532 wraplines function, CGI/Rexx, 275
The Workstation Group, 323 write function, BRexx, 365
X-Master, 435 write position, 68
XTM emulator, 428
760
W-X
Index
writech function, Regina, 339, 591
WWWSERVER_PROTOCOL special variable, 627
writeln function, Regina, 339, 591
WWWSERVER_ROOT special variable, Mod_Rexx, 627
WWWAddCookie function, Mod_Rexx, 623
WWWSERVER_SOFTWARE special variable, 627
WWWARGS special variable, Mod_Rexx, 626
WWWSetHeaderValue function, Mod_Rexx,624
WWWAUTH_TYPE special variable, Mod_Rexx, 626
WWWCnxRecAborted function, Mod_Rexx, 625 WWWSrvRec functions, Mod_Rexx, 625
WWWSub_Req_Lookup_File function, 624
WWWConstruct_URL function, Mod_Rexx, 623
WWWCONTENT_LENGTH special variable, Mod_Rexx, 626 WWWSub_Req_Lookup_URI function, Mod_Rexx, 624
WWWCONTENT_TYPE special variable, Mod_Rexx, 626 WWWUNPARSEDURI special variable, Mod_Rexx, 627
WWWCOOKIES special variable, Mod_Rexx, 626 WWWURI special variable, Mod_Rexx, 627
WWWDEFAULT_TYPE special variable, Mod_Rexx, 626
WWWEscape_Path function, Mod_Rexx, 623
WWWFILENAME special variable, Mod_Rexx, 626
X
WWWFNAMETEMPLATE special variable, Mod_Rexx, 626 x2b function, 97, 105, 570
WWWGATEWAY_INTERFACE special variable, 626 x2c function, 105, 570-571
WWWGetArgs function, Mod_Rexx, 286, 623 x2d function, 105, 571
WWWGetCookies function, Mod_Rexx, 623 XEDIT command, CMS,499
WWWGetVersion function, Mod_Rexx, 286, 623 XEDIT editor, 183, 505
WWWHOSTNAME special variable, Mod_Rexx, 626 XEDIT macros, VM Rexx, 496
WWWHTTP_time function, Mod_Rexx, 623 X- Master, 435
WWWHTTP_USER_ACCEPT special variable, 626 XML (Extensible Markup Language), 291-292. See also
RexxXML library
WWWHTTP_USER_AGENT special variable, 626
xmlAddAttribute function, RexxXML, 293, 297
WWWInternal_Redirect function, Mod_Rexx, 623
xmlAddComment function, RexxXML, 293, 297
WWWIS_MAIN_REQUEST special variable, 626
xmlAddElement function, RexxXML, 293, 297
WWWLogError function, Mod_Rexx, 623
xmlAddNode function, RexxXML, 293, 297
WWWLogInfo function, Mod_Rexx, 623
xmlAddPI function, RexxXML, 293, 297
WWWLogWarning function, Mod_Rexx, 624
xmlAddText function, RexxXML, 293, 297
WWWPATH_INFO special variable, Mod_Rexx, 626
xmlApplyStylesheet function, RexxXML,294, 299
WWWPATH_TRANSLATED special variable, 626
xmlCompileExpression function, RexxXML, 294
WWWPOST_STRING special variable, Mod_Rexx, 626
xmlCopyNode function, RexxXML, 293, 297
WWWQUERY_STRING special variable, Mod_Rexx, 627
xmlDropFuncs function, RexxXML, 293
WWWREMOTE_ADDR special variable, Mod_Rexx, 627
xmlDumpSchema function, RexxXML, 294, 298
WWWREMOTE_HOST special variable, Mod_Rexx, 627
xmlError function, RexxXML, 293, 298, 301
WWWREMOTE_IDENT special variable, Mod_Rexx, 627
xmlEvalExpression function, RexxXML, 294, 302
WWWREMOTE_USER special variable, Mod_Rexx, 627
wwwReqRec functions, Mod_Rexx, 624-625 xmlExpandNode function, RexxXML, 293, 297
WWWREQUEST_METHOD special variable, Mod_Rexx, 627 xmlFindNode function, RexxXML, 294, 298, 302
WWWRSPCOMPILER special variable, Mod_Rexx, 627 xmlFree function, RexxXML, 293
WWWRun_Sub_Req function, Mod_Rexx, 624 xmlFreeContext function, RexxXML, 294
WWWSCRIPT_NAME special variable, Mod_Rexx, 627 xmlFreeDoc function, RexxXML, 293, 296
WWWSendHTTPHeader function, Mod_Rexx, 286, 624 xmlFreeExpression function, RexxXML, 294
WWWSERVER_NAME special variable, Mod_Rexx, 627 xmlFreeSchema function, RexxXML, 294, 298
WWWSERVER_PORT special variable, Mod_Rexx, 627 xmlFreeStylesheet function, RexxXML,294, 299
XMLGenie! tool, r4 and roo! interpreters, 451
xmlGet function, RexxXML, 294
xmlLoadFuncs function, RexxXML, 293, 296
xmlNewContext function, RexxXML, 294
xmlNewDoc function, RexxXML, 293
761
X-Z
xmlNewHTML function, RexxXML, 293
xmlNodeContent function, RexxXML, 293 xmlRemoveContent function, RexxXML, 293, 297
xmlNodesetAdd function, RexxXML, 294 xmlRemoveNode function, RexxXML, 293, 297
xmlNodesetCount function, RexxXML, 294 xmlSaveDoc function, RexxXML, 293, 296
xmlNodesetItem function, RexxXML, 294, 298 xmlSetContext function, RexxXML, 294
xmlOutputMethod function, RexxXML, 294, 299 xmlValidateDoc function, RexxXML, 294, 298
xmlParseHTML function, RexxXML, 293, 301 xmlVersion function, RexxXML, 293, 301
xmlParseSchema function, RexxXML, 294, 298 XML and JSON, 689
xmlParseXML function, RexxXML, 293, 296, 298 X/Open CLI, 230, 232
xmlParseXSLT function, RexxXML, 294, 299 XPath, 292, 298
xmlPost function, RexxXML, 294 xrange function, 90, 96-97, 570
xmlRemoveAttribute function, RexxXML, 293, 297 XSLT (Extensible Stylesheet Language Transforma
tions), 292, 294, 299
XTM emulator, 424, 428-429
Z
Z shell and Regina, 326
762