tag:blogger.com,1999:blog-55122804567038429152024-02-20T14:01:35.671-05:00REXXpertiseTips, tricks, and notes on technique for devotees of the REXX programming language as developed, gathered, and outright-stolen during my over-quarter-century of writing in this marvelous tongue.rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.comBlogger41125tag:blogger.com,1999:blog-5512280456703842915.post-32397265861814545922022-09-19T13:24:00.000-04:002022-09-19T13:24:00.665-04:00Breathing Room<HTML>
<font face="Trebuchet, Verdana, Helvetica, Arial" size="3">
<!--
Breathing Room
-->
<p align=justify>
<p align=justify>
When I left my last contract, I had an idea for software that was —
to use an expression — 'half-baked'.
It was an idea for a problem reporting-responding system for your
local friendly tools maven — someone
like me.
Someone, perhaps, like you.
I thought there was a need for a vehicle for programmers — the folks who do the real work of producing
software that generates the reports and files that management uses on a day-to-day basis to run
the companies that produce the wealth that pays our salaries — to log that some particular tool
exhibits unusual behavior
or to suggest that some tool needs additional function.
<p align=justify>
The problem is that to develop mainframe software, one really needs a mainframe,
and when I say 'mainframe', what I really mean is an 'IBM-ish operating system'.
<p align=justify>
There aren't too very many of those around, and what few there are
are generally devoted to revenue-producing
activity, as opposed to blue-sky and pie-in-the-sky ventures...
like problem-reporting systems.
<p align=justify>
It's not easy to find such things — IBM-ish mainframes —
but it's not impossible, either, and I managed to find one
that didn't require me to pay $50/hr of computing time.
<p align=justify>
As a result, I've managed to get that problem reporting-responding system
pulled together into a form that
seems to me ready for some real-world testing.
So...<!--
FIXTOOL
TRAPOUT
LA
TBLGEN
TBLMSTR
AATBLID(IT)
AATBLNM(TOOLERR)
AAKEYS(ITTOOL ITRDATE ITRTIME ITRUSER)
AANAMES(ITRDESC ITFDATE ITFTIME ITFDESC ITFUSER ITIPARTY ITSTATE)
AADESC(Tool Problem Reports and Fixes)
AASORT( ITRDATE,N,D ITRTIME,N,D )
FCCMDUPD
-->
<p align=justify>
I have put 'FIXTOOL' out on my alternate source code page,
<a href="http://frankclarke.dx.am/REXX/listindx.html" target="_blank">
http://frankclarke.dx.am/REXX/listindx.html</a>,
and now invite all and sundry to steal a copy and run it through its paces,
reporting any errors back to me at <a href="mailto:rexxhead@yahoo.com">my main email address</a>.
You will also need to snag copies of several other routines that are needed in one way or another,
and FIXTOOL also requires that SMTPNOTE (an IBM-originated routine) be active on your site.
<ul align='justify'>
<li>
Routine DFLTTLIB should be downloaded and adjusted to fit your local environment.
It designates the local default ISPTLIB dataset that will house all the ISPF tables
used by my software.
Every user of FIXTOOL must be able to <u>WRITE</u> to its table.
</li> <li>
Routine ICEUSER should be downloaded and customized to identify the 'special' users
who are expected to <u>manage</u> the tools.
</li> <li>
Before running FIXTOOL, you must run TBLMSTR.
TBLMSTR will create an AA-form table (AAMSTR) with a single entry
describing itself, and write that table onto the default ISPTLIB.
</li> <li>
<u>You</u>, the maintainer, must add a row onto AAMSTR for the IT-type table
so that TBLGEN will know how to build it when ordered.
The easiest way to do this is to invoke FIXTOOL with the single parameter 'LOADQ'.
This only has to be done ONCE.
Subsequent invocations of FIXTOOL need not (should not) use LOADQ.
</li>
</ul>
<p align=justify>
So, here is the component list of all the software that must be in place in order to run FIXTOOL:
<ul>
<li>
DFLTTLIB
</li><li>FCCMDUPD
</li><li>FIXTOOL
</li><li>ICEUSER
</li><li>LA
</li><li>TBLGEN
</li><li>TBLMSTR
</li><li>TRAPOUT
</li>
</ul>
<p align=justify>
Several of these routines reference SYSUMON, a usage logging routine that uses RXVSAM to update a KSDS
where tool-usage statistics are kept.
You can either disable it or build the appropriate KSDS and let it count.
Up to you.
<p align=justify>
If you decide to fix any problems you find rather than have me fix them, I would still appreciate
hearing from you what those problems were and how you addressed them.
<p align=justify>
</font>
</HTML>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-83976692561478758392020-04-09T17:21:00.005-04:002022-05-18T11:55:17.590-04:00Quiescing<HTML>
<font face="Trebuchet, Verdana, Helvetica, Arial" size="3">
<!--
-->
<p align=justify>
Well, it's been a few years since I last had a contract, and I'm starting to think of myself as "really retired". That's not so bad. I had a good run, and if it's over, I'm not shedding any tears. There are several cruises on my to-do list, and not being tied to a desk has its advantages in respect to those. Besides, I'm 76. Prospective employers are probably risk-averse.
<p align=justify>
But I got to thinking — always a dangerous activity — that whereas most of my really good and really useful code is available at the <a href="https://www.rexxla.org/freerepo/REXX/" target="_blank">REXX Language Association</a>'s <u>free</u> code repository, there are some other code fragments that others may find useful or educational or entertaining, and maybe they should be made available, too.
<p align=justify>
I just finished packaging another few-hundred-K and I put them out on my
<a href="http://frankclarke.dx.am/REXX/listindx.html" target="_blank">regular website</a>. Check it out. Let me know what you think.
<p align=justify>
My email address is "rexxhead@yahoo.com".
<p align=justify>
<p align=justify>
P.s.: After losing (involuntarily) several previous websites, I finally found 'AwardSpace.com'
that offers a free 1Gb space for mounting one's personal webpage.
Now, for the sort of stuff <u><i>I</i></u> post, ASCII text and HTML,
1Gb will last me — for all practical purposes — effectively <u><i>forever</i></u>.
Thus, my new (old) personal webpage is at
<a href="http://frankclarke.dx.am/" target="_blank">http://frankclarke.dx.am/</a>.
If, anywhere below, you see a reference to a different website and clicking the link gives you a '404',
replace that website URL with "frankclarke.dx.am" and try again.
</font>
</HTML>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-91691832087066984012016-12-29T18:29:00.003-05:002024-01-09T16:36:13.195-05:00Saving Time; Saving Compiles<font face="Trebuchet, Verdana, Helvetica, Arial" size="3">
<!--
<h3>
Saving Time; Saving Compiles
</h3>
-->
<p align=justify>
Let's face it:
programs fail, and if they're like my programs, they'll fail at the most inopportune time,
generally around 2 in the morning.
<p align=justify>
A thought just occurred to me:
if you live or work in an environment where you are dependent on a <u>compiler</u> —
a PL/I, COBOL, or ForTran environment, for instance —
you need to be saving your compiler listings.
"OMG!
You can't be serious!
That would take up <u>mountains</u> of disk space!"
Yes, I'm serious.
The mountains of disk space are <u>peanuts</u> compared to the time your programmers will spend
at 2am recreating —
if it's even possible —
a compiler listing from before the last change to the <u>compiler</u>.
<p align=justify>
When some piece of compiled code <i>FOOPs</i> in the wee hours,
the last thing you need is to have to
locate the code and compile it.
The compile listing you get from doing that may not, in fact, be an accurate representation
of the load module that failed.
Comforting thought, no?
<p align=justify>
The only plausible solution is to save the compiler listing for the module you will eventually
roll into production.
Whenever you <u>last</u> produce a load module that is destined for production, <u>that</u>
compiler listing must be saved to a place where,
when you need it at 2am, you can find it quickly and easily.
How you pull this off is likely dependent on the system programmers who are in charge of
your <i>infrastructure</i>,
the processes you use to get software into the production libraries.
The greatest challenge here is to get those process-oriented folk to understand that the <i>process</i>
isn't just one step,
that actions taken in the past have serious consequences for actions that will take place in the
future.
Apologies in advance to sysprogs this doesn't apply to, but my experience is that most of them
are fundamentally incapable of thinking three moves ahead.
The compiler listing must be saved <u>now</u> in case it is needed in the future.
So...
<p align=justify>
If your protocol is that you always compile at 'acceptance test' time and <u>move</u> the
resultant load module to production when it's accepted,
you must capture the a/t compiler listing.
If you re-compile before the move to production, <u>that</u> compiler listing is the one
you need to preserve.
You need a <u>large</u> compiler-listing dataset whose members will match one-to-one with the
production load library.
You will also want to capture the linkeditor's output since that forms a critical
mass of information about the load module, and therein lies a problem:
the DCB of the LKED listing is potentially different than that of the compiler output.
I got yer solution right here:
<p align=justify>
Go to the REXX Language Assn's free code repository and get
<a href="http://www.rexxla.org/freerepo/REXX/combine.txt" target="_blank">COMBINE</a>.
In the JCL for doing the compile/link, insert an IKJEFT01 step after the LKED has run
and execute COMBINE there specifying the DDs for the compiler output and the LKED output.
COMBINE will combine (naturally) those two files and produce a single file that can be saved to
your listing dataset.
See the HELP text for COMBINE for the calling sequence, but it will generally be something like:
<blockquote>
COMBINE COMPILE LKED
</blockquote>
with //COMPILE DD referencing the compiler output and //LKED DD referencing the LKED output.
Your combined output will be on //$PRINT DD.
<p align=justify>
Start today.
Nothing moved to production before you start doing this will be included,
so the sooner you start, the better.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-39286354531945838722016-06-08T10:32:00.002-04:002022-05-18T12:00:59.875-04:00Profiles Inefficiency<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
...
</title>
</head>
<body>
<h3>
Profiles Inefficiency
</h3>
-->
<p align=justify>
I got into this racket in 1971 when IBM decided I should be a programmer rather than an accountant,
and who was I to argue?
They introduced me to the IBM 029 keypunch machine and the PL/I(F) language along with JCL and utilities.
I began to program, such as it was.
Write a description of the program, convert it to a flowchart, code the program by hand onto keypunch sheets,
give the sheets to the keypunch operator and wait for her (always 'her') to produce a deck of cards
with neat rectangular holes, proof-read the cards, deliver them to the RJE (Remote Job Entry) station downstairs
(we had a Mod 25, I think, that was used as a glorified card reader),
pick up the output when ready along with the cards,
check the SYSOUT, correct the program, repeat until success.
<p align=justify>
A few years later, the department got a shipment of 2741 terminals, Selectric-y things with the ability to communicate
with computers far away.
There was a sign-up sheet where one would bid for time on the few terminals, and we would wait
in our offices (yes, real offices) for the phone to ring with
the news that it was 'time' to go work on the terminal.
Log on to TSO, edit the datasets, compile the program, build the JCL, run it, check the output, fix the program,
fix the JCL, repeat until success.
Later, the 2741s were replaced by 3274s, 'green screen' paperless terminals, but the process, while faster,
remained essentially the same.
<p align=justify>
Then, along came SPF, the Structured Programming Facility, with a suite of utility functions and an editor...
a WYSIWYG editor!
What You See Is What You Get.
Each improvement made the process easier and faster and less error-prone.
It was a long time before anyone realized those three things were operationally connected.
<p align=justify>
SPF mutated into ISPF, the Interactive System Productivity Facility, and with it came a whole raft of new features...
and new problems.
One of the new features was something called "edit profiles".
ISPF would handle different types of data differently, and the user could control this by strategically naming
datasets.
The profiles were named based on the low-level qualifier (LLQ) of the particular dataset
plus its record format (F or V)
plus its record length, so you wouldn't get a PLI-F-80 profile mixed up with a CNTL-F-80 profile,
even though the data had the same general 'shape'.
Alas, ISPF only made provision for 25 profiles, and when a user created a 26th profile, the least-recently-used
profile would be summarily discarded to make room for the new one.
Because users were never given much education in 'profiles and their care and feeding',
and since the system administrators were often system programmers whose use of ISPF was rarely more
than rudimentary, the stage was set for all kinds of mischief:
<p align=justify>
The Jones Company uses COBOL and PLI, along with CLIST and REXX, in a zOS/TSO setting.
Everyone uses ISPF.
There are no standards regarding dataset names that deal with other than the first two nodes;
the LLQ is never even considered as something that <u>ought</u> to be standardized.
<p align=justify>
Arthur is a programmer.
He has datasets named (we ignore here the high-level qualifiers and concentrate on the LLQs)
PAYROLL.PLI, TOOLS.PLI, REPORTS.PLI, and MISC.PLI.
They all contain PL/I source and all use the same profile, PLI-F-80.
He also has PAYROLL.CNTL, TOOLS.CNTL, REPORTS.CNTL, and MISC.CNTL.
These all contain JCL and similar data and all use the same profile, CNTL-F-80.
Betty has datasets named PLICODE.STUFF, COBOL.STUFF, and JCL.STUFF.
They all use the same profile, STUFF-F-80, even though their contents are radically different.
Betty is constantly changing profiles in ISPF Edit to get the behavior she wants from the editor.
Cheryl has datasets named ACCTG.SOURCE (a mixture of PL/I and COBOL), and PROTOTYP.SRC (a similar
mixture of languages) along with ACCTG.JCL and PROTOTYP.JCLLIB.
Cheryl has four profiles: SOURCE-F-80, SRC-F-80, JCL-F-80, and JCLLIB-F-80.
Betty asks Cheryl to look at a module in COBOL.STUFF that won't compile in hopes that Cheryl might see
something wrong.
Cheryl views Betty's COBOL.STUFF and suddenly gets a new profile, STUFF-F-80, that she never had before,
and it's different than Betty's STUFF-F-80.
Cheryl is stumped by the problem and asks for Arthur's help.
Presently, Arthur has a STUFF-F-80 profile and his, too, is different than either Betty's or Cheryl's.
Neither Arthur nor Cheryl edited Betty's data;
they both used VIEW which, unfortunately, is affected by the problem since it uses edit profiles.
<p align=justify>
We're dealing here with just three people and already the problem is showing its potential.
Imagine a setting with 200 programmers, technicians, testers, and so on, all operating guidance-free.
One day you go into edit on one of your datasets and the highlighting is wrong, the data is shifted into
all-CAPS whenever you type something new, and your tab settings seem to have gone away.
"What the hell happened to my profile?" you ask.
The answer is that it got purged when Larry had you look at a stack of ISPF skeletons he found in an archive.
You created profile #26 and lost one that you had counted on keeping.
P.s.:
when you went back into edit on that all-wrong data you re-created a profile for <u>that</u>
dataset and purged another, different profile.
Which one?
I have no idea and neither does anyone else.
<p align=justify>
Is there an answer to the problem of "where did my profile go?"?
There is, and the answer is 'standards'.
Someone in authority — and the higher, the better — must say:
"All <i>xxx</i> data must reside in a dataset whose LLQ is <i>aaa</i>" and repeat as necessary for
all known common types of corporate data.
Exceptions, where a case can be made for an exception, are granted by managers.
There also needs to be a catch-all category for material that doesn't fit neatly anywhere else.
<p align=justify>
In recent years, ISPF changed the protocol a little.
Profiles can be locked or unlocked.
When EDIT goes looking for a profile to purge, it selects the least-recently-used <u>unlocked</u> profile.
If there aren't any of those, the least-recently-used <u>locked</u> profile gets tossed.
Also, there's something the sysprogs can do post-installation that allows each user to have 37 profiles,
somewhat easing the problem if not eliminating it.
Regardless, be careful about how you name your datasets and urge everyone else to be just as careful.
If corporate doesn't set standards, try to get your department or division to do it.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-4184722874381183412016-05-22T17:39:00.001-04:002022-05-18T12:02:01.887-04:00Quick! Get me member (Q47MMT1)!<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
...
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>
Imagine a partitioned dataset with 8,000 members (or more).
This is getting into the range where finding the directory entry for a specific member is becoming a real chore
and is chewing up cycles.
I heard of an imaginative way to speed up the process.
<p align=justify>
Define the partitioned dataset as a Generation Data Group and make the group large enough that,
when the dataset is split, searching the directory of each is less of a chore (it <u>will</u> <u>be</u>
even if only because each fragment of the whole is smaller).
Let's say, for the sake of argument, that we break it into 27 generations, one for each letter of the alphabet
plus a catch-all.
Now copy all the members beginning with non-alphabetics into generation #1, all the "A"s into #2,
all the "B"s into #3, etc.
When you access the group via its base-name (without specifying a generation) you get them all
concatenated in 27-26-25...3-2-1 order.
<p align=justify>
When you look for member 'Q47MMT1', the directory of generation #27 is scanned, but member names are always in
alphabetical order and this directory starts with 'Z...'.
That's not it; skip to G0026V00.
Its first entry starts with 'Y...'.
Nope.
G0025V00 starts with 'X...', G0024V00 starts with 'W...', G0023V00 starts with 'V...', G0022V00 starts with 'U...',
G0021V00 starts with 'T...', G0020V00 starts with 'S...', G0019V00 starts with 'R...', G0018V00 starts with 'Q...'.
Got it!
You quickly find the required member and processing continues.
What's happening here is that instead of searching through 8,000+ directory entries and finding
what you seek in (on average) 4000-or-so lookups, you looked at (on
average) 13 + ~150 (8000 / 27 / 2).
As the original partitioned dataset gathers more members, this comparison gets more stark.
At some point, the comparison is <u>so</u> stark that someone will wonder if the quicker method failed
because it just couldn't complete <u>that</u> fast.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-85637540027656741002016-05-09T17:47:00.002-04:002022-05-18T12:02:29.525-04:00ALIAS is not a four-letter word<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
ALIAS is not a four-letter word
-->
<p align=justify>
Are you one of those who thinks "Alias?
Why bother?"?
They <u>do</u> have their uses, and with a little imagination they can be leveraged to deliver surprising
productivity gains.
<p align=justify>
Aliases come in two flavors:
member aliases and dataset aliases.
<p align=justify>
Member aliases are nothing more than entries in a partitioned dataset's directory.
Each such entry holds the TTR (track and record) of an existing member — called the "base member".
If you edit an alias and save it, BPAM writes the saved text at the back of the dataset and records the new TTR
in the directory entry for the alias, making it a base member in its own right
(no longer an alias of some other base member).
But as long as it <u>is</u> an alias, any reference to the base name or any of its aliases points
to the same code.
Most languages have the facility of knowing by which name the routine was called,
and the logic may branch differently for each (or not — it depends).
<p align=justify>
Dataset aliases provide the same sort of facility but at the dataset level.
These aliases must be kept in the same catalog as holds the dataset name for which the alias is created.
The kicker here is that the alias and the dataset it aliases must have the same high-level qualifier.
If they didn't, they'd be in different master catalogs and <u>couldn't</u> exist in the same sub-catalog.
<p align=justify>
So, what can you do with a dataset alias?
Why would you bother?
Well, here's a practical application that can save hours of updating and weeks of grief:
You have a dataset (or a series of datasets) that IBM or some other maintainer periodically updates.
Maybe it's the PL/I compiler or something similar.
You have a cataloged procedure, a PROC, or possibly several of them that programmers use to compile programs.
If your PROC(s) all reference SYS1.COMPLIB.R012V04.LOADLIB, then when IBM sends down the next update,
somebody is going to have to change all those PROCs to reference ...R013V01... and slip them into the PROCLIB
at exactly the right moment.
Usually that means Sunday afternoon when the system is quiesced for maintenance and only the
sysprogs are doing any work.
Or...
<p align=justify>
You <u>could</u> alias whichever is the currently supported version as SYS1.COMPLIB.<b>CURRENT</b>.LOADLIB.
When the new version has been adequately tested and is ready to be installed for everyone's use,
you use IDCAMS to DELETE ALIAS the old one and DEFINE ALIAS the new one.
These two operations will happen so fast it will be like flipping a switch:
one instant everyone is using R012V04, and the next they're using R013V01.
The system doesn't even have to be down.
You can do it Tuesday during lunch.
Nobody's JCL has to change, but (more importantly) none of the PROCs have to change, either.
Your favorite beta-testers can access the next level just by overriding the STEPLIB.
Everybody else just uses the PROC as-is.
If somebody reallyreallyreally needs to get to the prior version, that, too, is just a STEPLIB
override.
<p align=justify>
I think (but I don't know for certain) that you can write an ACF2 rule that allows certain privileges to
an ALIAS that are prohibited to the BASE (and vice versa),
but the most amazing ALIAS-trick (as far as I'm concerned)
is the ability to swap one dataset for another with none of the users being any the wiser.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-72761709175004611302014-03-22T21:15:00.001-04:002022-05-18T12:02:51.829-04:00Proverbs 27:17<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
Proverbs 27:17
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>One of my favorite sayings is "God made programmers in pairs for a reason." Some of my friends will recall me telling them why it's so important that programmers review other programmers' code — and why it's so important that they have other programmers review <u>their</u> code: iron sharpens iron, the lesson of Proverbs 27:17.
<p align=justify>
"Gee," I can hear some of my friends exclaim, "I didn't think Frank was that into Scripture!" Relax, I'm as ignorant of Scripture as you suspected I was, but I <u>do</u> have friends who are otherwise endowed.
<p align=justify>
First, however, the background: I remember seeing my Mom getting ready to slice the Sunday roast: she would take two carving knives and whisk them rapidly together — <i>snick snick snick snick</i> — blade-to-blade alternating edges. Both knives got sharp. I didn't know why, and it wasn't important until many years after she left us.
<p align=justify>
When I mentioned this technique to a friend and colleague, Don Ohlin, years later as a justification for holding informal (at least) code reviews, his response was "Proverbs 27:17." As you might have anticipated, my response was "Huh?"
<p align=justify>
"As iron sharpens iron, so one person sharpens another," he replied.
<p align=justify>
Whatever your opinion of the Bible, there's a heap o' wisdom nestled within.
<p align=justify>
Now, schedule the damn code review and stop stalling.
<p align=justify>
<p align=justify>
P.s.: God made parents in pairs for very much the same reason.
<p align=justify>
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com1tag:blogger.com,1999:blog-5512280456703842915.post-19860754447314722682013-10-24T13:28:00.001-04:002022-05-18T12:26:58.743-04:00Retrospective<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
Retrospective
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>
So, here I am on the feather-edge of retirement (I'll be 70 in a few months) and I'm still learning things. I had an insight last night that kept me awake mulling it. My last contract was with Bank of America in Texas and, while it was fun, it was also more than just a little frustrating.
<p align=justify>
When I first started looking at the code I would be working with at BofA, I was confused. Everybody these days writes 'strictly structured', right? No, wrong, and that was what was so confusing. Last night's insight cleared away all the cobwebs... just in time for Hallowe'en.
<p align=justify>
<p align=justify>
There are two ways to approach a programming problem. In the first, you start out by assuming that this is an easy problem; you have to do A, B, C, and finally D. Voila! You sit down and write code and it comes out as a single long module. It may be fairly complex. (This was typical for most of the BofA code I saw.)
<p align=justify>
If, instead, you assume that the problem will be complex, that you have to do A, B, C, and finally D, you will sit down and write a top-level module with stubs for the called subroutines. Then you will write the innards of the subroutines, probably as routers with stubs for <u>their</u> called subroutines. This process will continue through <i>n</i> levels until each subroutine is so simple it just doesn't make sense to break it down further. The resulting program will be longish, but (all things considered) pretty simple regardless of the initial estimate of complexity.
<p align=justify>
I (almost) always presume a programming task will be complex. If that turns out to be wrong, no big loss. If I were to assume some programming task were simple and it turns out <u>not</u> to be quite as simple as I originally thought — that would hurt. It would hurt because halfway through writing that 'one long module', I would discover the need for the same code I used in lines 47 through 101. Stop. Grab a copy of that code. Create a subroutine at the end of the code. Insert CALLs at both places. Continue writing that 'one long module' where you left off.
<p align=justify>
If that scene happens more than once or twice, what we wind up with is a long main module with several calls to randomly-placed subroutines. The coefficient of complexity has just been bumped up, and the bump could be quite a lot. If it's one of the newly-created subroutines whose function needs to be partitioned, the code soon takes on a distinct air of 'disorganization'.
<p align=justify>
Do I have to point out that there's way too much overly-complex and disorganized code out there and running in production? No, I probably don't; we've all experienced Windows.
<p align=justify>
So, there's a built-in penalty for assuming simplicity, and it turns out this penalty applies (in REXX, at any rate) <u>no</u> <u>matter</u> <u>how</u> <u>complex</u> the eventual program <u>actually</u> <u>is</u>.
<p align=justify>
If a (REXX) program is written as 'one long module', possibly with a few random subroutines for function required in more than one place, diagnosis becomes a problem. Unless the programmer has anticipated bypassing iterative loops, a trace will have to endure every iteration in every loop before getting to the next stage. To avoid this most painful experience, what happens most often with such code is a quick one-time fix to turn TRACE on here and shut it off there. But then, the program being diagnosed is no longer the program that failed; it's a modified version of the failing program.
<p align=justify>
If a (REXX) program is highly-structured, function will be very encapsulated to the point that any error will be isolated to one or a very small number of suspect segments. Running such a heavily-encapsulated program in trace-mode means that entire trees of logic can be bypassed: if TRACE is <u>on</u> for a higher-level module, it can be turned <u>off</u> in a submodule (and all its children) but will still be <u>on</u> when control returns to the higher-level module. The more structured the code, the easier it is to debug. With one <i>proviso</i>...
<p align=justify>
You can have a highly-structured program that is nevertheless disorganized. If, for example, you place your subroutines in alphabetical order, the flow of control will appear chaotic. Ideally, submodules that are merely segments of an upper-level router should appear in roughly their order-of-execution. Although they're broken out into separate segments, they still retain the flavor of that 'one long module' insofar as they appear one after the other like the cars of a train. Reading such code becomes easier because a CALL to a submodule is a call to code which is (probably) physically close by. (This is not always strictly true.)
<p align=justify>
COBOL programmers long ago adopted a more-or-less universal convention: they prefix the name of each code segment with a glyph ('D100', perhaps) that indicates its logical position in the complete program. A COBOL programmer seeing a reference to 'D100-something' in module 'C850-GET-USER-ID' knows to look <u>later</u> in the code for that segment. The same technique works equally well in all languages, and REXX is not an exception. (I tend to use alpha-only such that the mainline calls A_INIT, B_something, etc. Module C_whatever calls CA_blah, CB_blah, etc. Whatever works...)
<p align=justify>
Exactly the same sorts of things can be said about modifying an existing program. The 'one long module' requires careful planning and skillful execution when inserting new function or changes to existing function. Testing the new function is a chore to the same extent diagnosing an error is a chore, and for the same reasons. Highly-structured code is designed to be modified; it was written that way.
<p align=justify>
Summarizing: a highly-structured REXX program may be a little longer than it (strictly) has to be, but it will be easier to understand and easier to diagnose in case of an error. This understanding can be enhanced by strategic naming of segments and by arranging the segments to more closely align with the actual order of execution.
<p align=justify>
Recommendation: Structure is your friend. It may be your best friend.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com4tag:blogger.com,1999:blog-5512280456703842915.post-10841117707490732612012-08-21T00:38:00.000-04:002012-08-21T14:26:46.170-04:00What's Wrong With This picture?<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
...
</title>
</head>
<body>
<h3>
What's Wrong With This picture?
</h3>
-->
<p align=justify>How many times have you seen this and thought nothing of it?
<p align=justify>
<pre>
"EXECIO * DISKR FIRST (STEM FIRST. FINIS"
"EXECIO * DISKR NEXT (STEM NEXT. FINIS"
do nn = 1 to next.0
--parse token from next.nn --
--35 lines of binary search thru first. --
if notfound then do
--diagnostic message--
end
else do
--process the match--
end
end
</pre>
<p align=justify>Not only have I <u>seen</u> this kind of code, I have <u>written</u> this kind of code. A startling revelation, an epiphany, has rocked my world.
<p align=justify>The 'revelation' is this: in MVS, the first time you say 'READ', you don't just read <u>one</u> record; you read five <u>buffers</u>. If you're talking about modern DASD and 80-byte records, you've just 'read' something like 1400 or 1500 records. They're all in main storage. Whatever operation you do to them, you do at main storage speeds. And "EXECIO * DISKR" doesn't stop at five buffers; it reads <u>all</u> of it. All the heavy lifting, the SIOs, has already been done. You've just spent $11 to read from DASD, and now you propose to save four cents by doing a binary search. Are we all nuts?
<p align=justify>In a situation like this, we should leverage the power of REXX's data-associated arrays by spinning through one of those stems and parsing everything we can find, then use the data-association to the second file to know (intuitively) whether there is or is not a match. It's all in main storage, right? You would need highly-sophisticated and very expensive equipment to discern how much time you saved by doing a binary search over using a sequential process. The cost of having a programmer write those thirty-five lines of binary search will <u>never</u> be paid back by the time saved.
<p align=justify>Edsger Dijkstra once proposed that a programmer worrying about how fast (or slow) hir code would execute was worrying about the wrong thing. "Get a faster computer", he advised. Easier said than done in many cases, but always the optimal solution.
<p align=justify>That's not, however, what we're seeing here. This is truly "penny-wise and pound-foolish" to do all that I/O and then waste the advantage of having it all immediately accessible (let's face it) for free.
<p align=justify>I think I may have written my last binary search. What do you think?
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com2tag:blogger.com,1999:blog-5512280456703842915.post-60891851346838253362012-08-10T19:46:00.001-04:002021-01-08T13:00:37.860-05:00Embedding ISPF (and other) assets<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
DEIMBED embed imbed
<title>
Embedding ISPF (and other) assets
</title>
-->
<p align=justify>When I write a tool for myself — for my own use — I will typically include ISPF assets at the bottom of the code and have software extract them as part of the initialization phase. I rarely load panel text to ISPPLIB or skeletons to ISPSLIB. There are several advantages to keeping your ISPF assets 'local':
<ul>
<li>I/O is reduced, sometimes <u>very</u> substantially reduced
<li>changes made to these assets are reflected <u>immediately</u> without having to be in TEST-mode and certainly without having to leave ISPF and restart it
<li>there is no doubt about the identity of subsidiary elements
<li>there is no danger of duplicate member names
<li>When distributing or installing, there is only <u>one</u> element to distribute or install: the enclosing REXX code
</ul>
<p align=justify>When ISPF is invoked by a non-developer (call it 'standard mode') its habit is to <i>cache</i> any panels, skeletons, or messages that it uses. It keeps them in storage so that if the same element is re-used, ISPF can get it from the cache rather than doing I/O to get a fresh copy. Obviously, if you're modifying that element, saving it to its library won't do a thing for your current session. To get that new panel, you have to exit ISPF to READY-mode and restart ISPF. <u>That</u> takes a lot of I/O because on start-up, ISPF opens and reads ISPPLIB, ISPSLIB, ISPTLIB, ISPMLIB, and ISPLLIB so that it knows all the available membernames and where they're located — in case you ask for one of them.
<p align=justify>Developers who work with ISPF services generally invoke ISPF in TEST-mode when developing because in TEST-mode, ISPF caches nothing and <u>always</u> does I/O to handle service requests. If you've just saved a change to a panel and you're in TEST-mode, your next DISPLAY request will retrieve the new version. The penalty you pay for this is that <u>every</u> service request is handled via I/O.
<p align=justify>Embedding your ISPF assets gives you the best of both worlds: because ISPF caches elements based on DSN+membername, re-extracting ISPF assets at execution-time creates a new dataset (in VIO) and ISPF recognizes that <u>this</u> member <i>XYZ</i> is not the same <i>XYZ</i> as that in the cache, so it reloads a fresh copy. All other service requests are handled via the cache — because you don't use TEST-mode.
<p align=justify>It gets better: When you invoke the enclosing REXX, it all gets read into storage immediately, and that includes your panels and skeletons. Extracting them thus happens at 'core speed' and if they're written to VIO datasets, <u>that</u> happens at 'core speed' as well. No need to read the panel(s) from ISPPLIB or the skeleton(s) from ISPSLIB separately. It's already here.
<p align=justify>You no longer have to search through the libraries to ensure you're not using a duplicate membername — because your data is not going to live in any of those libraries. The elements embedded in your application are extracted and loaded to a library which is then LIBDEF'd into a preferential position to all others. If you use a name replicated elsewhere, you will only use <u>your</u> element; the other will be 'masked' by virtue of being too far down the concatenation. Of course, when the application ends, LIBDEF tears down those purpose-built libraries and the environment is restored to its pre-invocation state. Neat.
<p align=justify>When you install the application, you install <u>one</u> element. All the other panels and skeletons used by the main REXX routine do not get separately installed — there's no need to formally install them because they will be regenerated dynamically as and when needed.
<p align=justify>If anyone out there can find a 'down-side' to any of this, I'd be very interested to hear it. To me, it all looks like 'up-side'.
<p align=justify>Code for extracting ISPF assets can be found on my <a href='http://www.rexxla.org/freerepo/REXX//deimbed.txt' target="_blank">REXX Tools page</a> and a short example of how it's implemented at <a href='http://www.rexxla.org/freerepo/REXX//alist.txt' target="_blank">ALIST</a>.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com3tag:blogger.com,1999:blog-5512280456703842915.post-19533293151023171472012-05-07T23:00:00.003-04:002022-05-18T12:29:08.939-04:00Scrolling a Panel Left and Right<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
Scrolling a Panel Left and Right
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Back on November 3rd, I wrote about "cylindrical arrays" and said "ISPF facilities do not allow the shifting of panels right and left except for some very special IBM-originated panels", but Chris Lewis introduced me to a way to <I>simulate</i> it. Herewith.
<p align=justify>The first thing is to rig the panel so that it can have any column headings you desire and fit it with a variable )MODEL. The "desc" following is the last line of the )BODY and is where the column headings would be stored. It is followed by a )MODEL section where the actual model is a variable:
<pre>
@desc
)MODEL ROWS(SCAN)
&modl
</pre>
<p align=justify>Before the panel can be TBDISPL'd, values for those must be set up...
<pre>
/* Set up values for the variable-format panel(s) */
desc1 = "V -Member- Type A/R Base -Userid- ---Date--- Time Li",
|| "btype Cleared"
desc2 = "V -Member- Type A/R Base -Userid- -Username---- Cl",
|| "eared As"
modl1 = "_z!mlroot +!z !mlbase !mluid !mldate !mltime!ml",
|| "loc !mlclrdt "
modl2 = "_z!mlroot +!z !mlbase !mluid !mlsname !m",
|| "lclrdt !mlclrnm "
</pre>
<p align=justify>...and loaded to the variables named on the screen:
<pre>
modl = Value('modl'style)
desc = Value('desc'style)
</pre>
<p align=justify>Now, whenever the response from the panel is a PF10 or PF11, call the code that inches the "cylindrical array" left or right:
<pre>
select
when pfkey = "PF11" then,
style = style//stylect + 1 /* next style */
when pfkey = "PF10" then,
style = (style+stylect-2)//stylect + 1 /* prev style */
otherwise nop
end
</pre>
<p align=justify>That's all there is to it. Once STYLE is set based on the PF10 or PF11, that style-number is used to select DESC1/MODL1 or DESC2/MODL2 just before the panel is redisplayed. It's the same panel (really), but the content displayed there will be different enough that it will <u>appear</u> the panel has actually been shifted.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com3tag:blogger.com,1999:blog-5512280456703842915.post-58336073691035723532011-12-04T12:06:00.002-05:002022-05-18T12:30:09.335-04:00File Tailoring for Fun and Profit<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
File Tailoring for Fun and Profit
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Creating JCL for running a mainframe task is the most common use for ISPF's File Tailoring — because it does it so well. Too often, though, such tasks are approached with a 'Bigger Hammer' mentality because the process to be tailored is seen as uncomfortably complex. For instance: your installation uses COBOL, PL/I, and ForTran; some programs use DB2; some run as part of CICS. When it comes time to compile something, you <u>may</u> have to run the DB2 preprocessor; you <u>may</u> have to run the CICS preprocessor; the compiler must be appropriate to the source language; if the compile is successful, the object module is to be link-edited; much of the compiler listing for DB2/CICS modules is crap that does little beyond making the printed version harder to lift. Is the compiler listing to be saved as a dataset or just printed? If 'saved', where? Are you going to trim away all the frou-frou before you print or save?
<p align=justify>Faced with such a collection of decisions, most installations opt to have individual PROCs for each potential combination and to name them under a convention that allows the programmer to correctly specify the name of the PROC that does a DB2/non-CICS/PLI compile-and-link. When a new compiler is released, somebody gets to fix and test a dozen PROCs to insert the updated library information, and it's usually several weeks before anyone realizes that an error sneaked through. Ouch.
<p align=justify>The reason file tailoring is not considered in these instances is because of all those <i>may</i>s above. The DB2 precompile hands its output to the CICS precompile, but there <i>may not be</i> a DB2 precompile step at all. The compiler gets its input from the CICS precompile step... unless there <i>isn't</i> a CICS precompile step. Where does the input come from <u>then</u>? There <u>is</u> a solution.
<p align=justify>You always start off with an IEFBR14, the safest program in the visible universe:
<pre>
//ORIGIN EXEC PGM=IEFBR14
//SOURCE DD DISP=SHR,DSN=&SRCDS(&SRC)
)SET LSTEP = ORIGIN
)SET LDDN = SOURCE
</pre>
<p align=justify>Note that this piece of skeleton identifies itself by setting LSTEP and LDDN. Later steps can use these to find the input they need to focus upon. Let's posit that if we have to ZORK and PARFLE and FLENCH a program, they always happen in that order, although some (or none) of them may happen at all.
<pre>
)SEL &ZORK = Y
//* --------------- ZORK processor -------------- */
//ZORK EXEC PGM=ZORK
//SYSIN DD DSN=*.&LSTEP..&LDDN
//SYSPRINT DD SYSOUT=*
//SHIPOUT DD UNIT=VIO,SPACE=(CYL,(5,1)),DISP=(,PASS),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
)SET LSTEP = ZORK
)SET LDDN = SHIPOUT
// IF (ZORK.RC << 8) THEN
)ENDSEL &ZORK = Y
)SEL &PARFLE = Y
//* --------------- PARFLE processor ------------ */
//PARFLE EXEC PGM=PARFLV2
//PARFIN DD DSN=*.&LSTEP..&LDDN
//SYSPRINT DD SYSOUT=*
//PARFOUT DD UNIT=VIO,SPACE=(CYL,(5,1)),DISP=(,PASS),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
)SET LSTEP = PARFLE
)SET LDDN = PARFOUT
// IF (PARFLE.RC << 4) THEN
)ENDSEL &PARFLE = Y
)SEL &FLENCH = Y
//* --------------- FLENCH processor ------------ */
//FLENCH EXEC PGM=FLENCH00
//SYSIN DD DSN=*.&LSTEP..&LDDN
//SYSPRINT DD SYSOUT=*
//OUTFLEN DD UNIT=VIO,SPACE=(CYL,(5,1)),DISP=(,PASS),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
)SET LSTEP = FLENCH
)SET LDDN = OUTFLEN
// IF (FLENCH.RC << 8) THEN
)ENDSEL &FLENCH = Y
</pre>
<p align=justify><u>If</u> the ZORK processor is activated, it gets its input from '*.ORIGIN.SOURCE' because those are the current settings for LSTEP and LDDN. When it's complete, it indicates that any later steps should use ZORK.SHIPOUT by setting LSTEP to 'ZORK' and LDDN to 'SHIPOUT'. If the ZORK processor is <u>not</u> activated, LSTEP remains set at 'ORIGIN' and LDDN at 'SOURCE'.
<p align=justify>If <u>only</u> the FLENCH processor is cleared to run, neither of the other two steps have altered LSTEP or LDDN and it picks up the dataset mentioned by the IEFBR14 step as its input. Whichever combination of steps ran or didn't, the next step downstream will <u>always</u> find its input via '*.&LSTEP..&LDDN'.
<p align=justify>Of course, things could be more complicated necessitating a little more <i>finesse</i>, but File Tailoring is, I assure you, up to the task.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-14380808973086373882011-12-01T12:29:00.001-05:002011-12-01T12:30:23.094-05:00What about you?<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
What about you?
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>I'm still reviewing old code looking for techniques that might be either useful or entertaining. It occurs to me that readers may have tips of their own they'd like to pass along. Anybody? Bueller?
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-15074277772432103612011-11-29T08:41:00.001-05:002011-11-29T08:44:46.393-05:00INTERPRET?? WDNNS INTERPRET!<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
INTERPRET?? WDNNS INTERPRET!
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>(For those who don't know what 'wdnns' means, it is a reference to a misquote of a line from <i>Treasure of the Sierra Madre</i> and for the rest, you can look it up on the web. The expansion is <i>"We don' need no steenking ..."</i> 'Nuf said.)
<p align=justify>It is my firmly held conviction that <i>INTERPRET</i> is almost never required for ordinary REXX processing. Les Koehler has come up with one or two <i>scenaria</i> where it is <u>useful</u> but they are so bizarre that 'esoteric' is a risible understatement. Most uses of INTERPRET run something like this:
<pre>
/* 'parm' looks like ' name=Smith ' */
parse var parm tag "=" tagval
interpret tag "=" tagval
</pre>
<p align=justify>I hope I got that right. Since I never use INTERPRET and I don't have a mainframe to test this out, I have to guess as to what the code would actually look like. If I were writing this code, I would use VALUE instead.
<pre>
parse var parm tag '=' tagval
rc = Value(tag,tagval)
</pre>
<p align=justify>I have also seen entire commands constructed piece-by-piece and then <i>INTERPRET</i>ed to cause the command to be executed. Unnecessary.
<pre>
interpret "ISPEXEC TBCREATE" tblnm blah blah blah
</pre>
<p align=justify>I have <u>never</u> seen a case where the <i>INTERPRET</i> couldn't simply be converted to a straight execution:
<pre>
"ISPEXEC TBCREATE" tblnm blah blah blah
</pre>
<p align=justify>Anyone with a <u>true</u> instance of a <u>necessary</u> INTERPRET is welcome to present the same here. I'd love to be proven wrong.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com13tag:blogger.com,1999:blog-5512280456703842915.post-75805790677016840322011-11-28T09:39:00.001-05:002015-06-29T00:09:40.767-04:00When TRACE just isn't enough<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
When TRACE just isn't enough
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Back on November 1, I exhorted all REXX programmers to <u>always</u> make it possible to turn TRACE on via a parameter rather than making a code change to the program itself. There are many good reasons to support that position philosophically, but there's one that makes all others pale in comparison. When you can turn TRACE on remotely, you can trap the entire output of your program as it runs in TRACE.
<p align=justify>You set up an OUTTRAP, issue the command, and close the OUTTRAP when it finishes. 'Issuing the command' in this case means calling the routine in such a way that TRACE is on. If you can't turn on TRACE with a parameter, you add an entire layer of complexity that doesn't need to be there. If you're in tune with REXXSKEL, it's as simple as
<pre>
rc = Outtrap("OUT.")
(TSOCMD) "(( TRACE R"
rc = Outtrap("OFF")
</pre>
<p align=justify>Now, the entire TRACE output is stored in stem 'out.' (here's hoping you didn't blow out of your TSO region in the process...) and all that's left is to allocate a dataset to hold it, then
<pre>
"EXECIO" out.0 "DISKW TRAPOUT (STEM OUT. FINIS"
</pre>
<p align=justify>If you allocated the dataset large enough (I always ask for 'CYL,(5,5)') you now have a browseable dataset showing every last line of execution right up to the point of failure. You'll thank me for this when your routine crashes trying to process record number 11,728.
<p align=justify>Over on <a href="http://www.rexxla.org/freerepo/REXX//">my REXX page</a> you'll find 'TRAPOUT' that implements this simple, straight-forward method for anyone whose code can be coaxed into running in TRACE :-)
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com1tag:blogger.com,1999:blog-5512280456703842915.post-4691140780096200672011-11-27T11:46:00.003-05:002022-05-18T12:31:49.566-04:00ISPF Tables — Copying<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables — Copying
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>For any number of reasons, you may decide you need a copy (usually partial) of some ISPF table. Possibly, the subset of the table you wish to operate on may not be easily parsed on the basis of the table's data content. You will have to build a temporary table and load it from the original table, perhaps row-by-row. Careful, now...
<p align=justify>TBQUERY will tell you what an existing table looks like. As soon as the table is opened, TBQUERY can be invoked to give a list of the keys and names. From this, you can TBCREATE a replica table.
<pre>
"TBOPEN SAMPLE"
"TBQUERY SAMPLE KEYS(knames) NAMES(nnames)"
"TBCREATE NEWCOPY KEYS"knames "NAMES"nnames
</pre>
<p align=justify>The column names returned from TBQUERY are enclosed within parentheses. Unless you have other uses for the lists, it isn't even necessary to trim them off. At this point, you are ready to write a duplicate or subset table, save for one easily overlooked aspect.
<p align=justify>Remember 'extension variables'? Unless you ask when reading each input row you will not know whether there are extension variables attached to the row, nor will you know their names. Not knowing their names means not being able to specify which names should be attached to each output row.
<pre>
do forever
"TBSKIP" intbl "SAVENAME(xvars)"
if rc <> 0 then leave
"TBMOD " outtbl "SAVE"xvars
end
</pre>
<p align=justify>Notice that the extension variables themselves did not have to be operated on in any way. We did not have to assign them, for instance. Because the row was read (the NOREAD parameter was not specified), <u>all</u> the row's variables: keys, names, and extension; were populated to the variable pool. They are therefore immediately available to be loaded to the output table row.
<p align=justify>In fact, because of this behavior: populating every variable regardless of type; it might be good practice to <u>never</u> read a table row without acquiring the list of extension variables.
<p align=justify>You can get fancier than this, of course. For an idea of just how much fancier you can get, take a look at TBCOPY over on <a href="http://www.rexxla.org/freerepo/REXX//">my REXX page</a>. TBCOPY doesn't use TBQUERY. It relies on an external subroutine, TBLGEN, which itself relies on a special table in which is stored the DNA of all permanent tables, sort of a data dictionary for tables. TBLGEN is much more elaborate than is strictly necessary, because it is intended to do <u>all</u> of an installation's TBCREATEs for permanent tables. TBCOPY is likewise much more elaborate than is strictly necessary because it is intended to do <u>all</u> TBCOPYs for permanent tables within an installation.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-47968323577311539512011-11-26T11:45:00.002-05:002022-05-18T12:32:19.120-04:00ISPF Tables — Extension Variables<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables — Extension Variables
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Sometimes we just have to deal with tables we didn't design (and would have designed differently had we had the opportunity). Sometimes we just need <i>a tiny little tweak</i> to make that table perfect (reg., U.S. Patent Pending). Sometimes we want to let everyone know that <u>this</u> table row is unusual either for what it has or for what it hasn't. Enter the extension variable.
<p align=justify>Every table row in a given table has <i>room for</i> every KEY and NAME field originally specified when the table was TBCREATEd. Flexible little devils that they are, table rows also have unlimited<sup>*</sup> space for other stuff, and every table row can be unique as to what other stuff it holds. Observe:
<pre>
"TBCREATE BILOMAT KEYS(BMITEM) NAMES(BMDESCR BMONHAND BMECOQTY)"
</pre>
<p align=justify>our Bill-of-Materials table. It is keyed by the item number (BMITEM), and has fields for a description, the number on hand in inventory, and the economic order quantity. Wha...? Where are the 'materials' that make up the 'bill'? They're in extension variables, naturally.
<pre>
"TBVCLEAR BILOMAT"
bmitem = 'ER4590T'
bmdescr = 'Whatsis, large, left-handed, tan'
bmonhand = qty.whatsis_l_lh_tan
bmecoqty = ecocalc.whatsis_l_lh
xvars = 'DD114 DF33 DF34 DF36 DF38 EM2030'
dd114 = 6
df33 = 1
df34 = 1
df36 = 1
df38 = 2
em2030 = 4
"TBADD BILOMAT SAVE("xvars")"
</pre>
<p align=justify>The row for 'ER4590T' in table BILOMAT has all four canonical fields filled, and there are six additional extension variables, one each for the six components of every ER4590T. When the supplier for the EM2030 component advises you there will be an unavoidable 40% increase in the price of that component, you're going to want to know which of your products (BMITEMs) is going to be affected and by how much. The catalog entries for each of those products will have to be adjusted to correctly show the new price of everything that relies on the EM2030, right? Of course!
<pre>
"TBVCLEAR BILOMAT"
em2030 = 0
"TBSCAN BILOMAT ARGLIST(EM2030) CONDLIST(GT)"
"TBDISPL BILOMAT PANEL(SRCHPART) AUTOSEL(NO)"
</pre>
<p align=justify>TBVCLEAR zaps all the 'regular' names mentioned in the definition of table BILOMAT, variable 'EM2030' is set to zero, TBSCAN specifies that we seek all rows where an extension variable 'EM2030' has a value greater than zero. Panel SRCHPART should, if coded correctly, display a scrollable list of all rows (all BMITEM keys) associated with subassembly EM2030.
<p align=justify>Zounds! This is almost DB2! Except that it didn't take us eight months and require the approval of two vice-presidents. And if they decide later that they really <u>do</u> want to implement it in DB2, you already have the proof-of-concept ready to demonstrate.
<p align=justify>
<p align=justify>(*) — You didn't think when I said 'unlimited' I actually meant 'without limit', did you? Tsk. Of course there are limits. It's just that you're unlikely to hit them before your table is so large your system chokes trying to open it. I've seen that happen with complex tables as small as 6,000 rows, but I have also seen 50,000-row tables that worked just fine, and I suspect they could have grown much larger without causing a problem.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-25822423045075126212011-11-25T08:47:00.002-05:002022-05-18T12:32:35.500-04:00ISPF Tables — Searching<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables — Searching
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>There's no telling how many programmers have simply given up using ISPF tables because <i>'the @#$% table services never work the way you expect them to work!'</i> They actually work just fine. It's the @#$% IBM manuals that don't make their operation clear — don't make the operation of some very complex services as clear as they ought to be. Whether this is simply a matter of writing style or whether IBM does so deliberately, the delicate interrelationship between TBVCLEAR, TBSARG, TBSCAN, and TBDISPL rarely gets to a programmer's conscious level. The lack of examples showing TBVCLEAR, TBSARG, and TBDISPL working in concert also doesn't help.
<p align=justify>The most important ISPF table service related to table searching is neither TBSARG nor TBSCAN; it is TBVCLEAR. The IBM manuals (almost) completely gloss over the fact that when searching an ISPF table, every element that has a value, <u>whether</u> <u>it</u> <u>is</u> <u>part</u> <u>of</u> <u>the</u> <u>explicitly</u>-<u>named</u> <u>search</u> <u>argument</u> <u>or</u> <u>not</u>, participates in setting the search argument. Every element <u>that</u> <u>has</u> <u>a</u> <u>value</u>. The service that makes sure elements do not have values is TBVCLEAR.
<p align=justify>If you carefully read the manual text for TBSARG, there are subtle clues to this behavior as it relates to setting search/scan arguments:<blockquote><i>A value of null for one of the dialog variables means that the corresponding table variable is not to be examined during the search.</i></blockquote> and <blockquote><i>To set up a search argument, set table variables in the function pool to nulls by using TBVCLEAR.</i></blockquote> and <blockquote><i>The search arguments are specified in dialog variables that correspond to columns in the table, and this determines the columns that take place (sic) in the search.</i></blockquote>
<p align=justify>You could be forgiven for thinking that specifying this variable or that one in a TBSARG might have some bearing on which variables get used for setting the search argument. Alas, it has less 'bearing' than might seem obvious.
<p align=justify>Let us assume that we have a table, TBL, and that this table is defined as
<pre>
"TBCREATE TBL KEYS(KEY1 KEY2) NAMES(NAME1 NAME2 NAME3) WRITE REPLACE"
</pre>
<p align=justify>We have read a particular row, either by TBGET or TBSKIP, and now we wish to find <u>all</u> the rows in TBL with the same NAME2 value. These will be displayed via TBDISPL for further processing.
<pre>
"TBSARG TBL NAMECOND(NAME2,EQ)"
</pre>
<p align=justify>is insufficient. It is insufficient because KEY1, KEY2, NAME1, and NAME3 all have values and are not null. In fact, a subsequent TBSCAN or TBDISPL will return a single row, the one that was just read. That's the only row on the table with a matching KEY1 and KEY2. In order to expand the search to the remainder of the table, we must zap all those variables:
<pre>
nval = name2 /* preserve the value */
"TBVCLEAR TBL"
name2 = nval /* load NAME2 */
"TBSARG TBL NAMECOND(NAME2,EQ)"
</pre>
<p align=justify>In fact, since "EQ" is the default and all table variables that are not null participate in the search (and NAME2 is the only non-null variable at this point), it should be sufficient in this case to cast the TBSARG as
<pre>
"TBSARG TBL"
</pre>
<p align=justify>Since we're working in REXX, some may be tempted to DROP the elements that are not to be searched-for. A REXX 'drop' is not the same thing as setting a table variable to null via TBVCLEAR and the results may be disappointing. Likewise, setting the variables to null via 'closed quotes'
<pre>
parse value "" with key1 key2 name1 name3 .
</pre>
<p align=justify>also won't get you what you need. This will cause the search to be limited to those rows for which KEY1 and KEY2 are empty. There can only be one of those and there may not be even that.
<p align=justify>Before TBSARG, before TBSCAN (if that's how you're setting the search argument), TBVCLEAR. Voila!
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-26597310835933722112011-11-23T12:37:00.002-05:002022-05-18T12:32:49.637-04:00ISPF Tables — Closing<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables -- Closing
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>How do you like that? I wrote this and never published it. This was supposed to be the blog entry for 11/22, and it seems to have been forgotten. Ah, well... better late than never...
<p align=justify>When you've finished doing whatever needed to be done with the table, it must be TBCLOSEd. As with TBOPEN, a LIBDEF is needed, but not to ISPTLIB. ISPTLIB is the <u>input</u> side of table processing. ISP<u>TABL</u> is the <u>output</u> side of table processing. Since data will be written to ISPTABL, ISPTABL cannot be a concatenation. Only one dataset may be allocated to ISPTABL:
<pre>
if noupdt = 1 | sw.0tbl_chgd = 0 then "TBEND " $tn$ /* don't save */
else do
"LIBDEF ISPTABL DATASET ID("isptabl") STACK"
"TBCLOSE" $tn$
"LIBDEF ISPTABL"
end
</pre>
<p align=justify>If there have been no changes to the table, it is not necessary to TBCLOSE (which writes the table to disk). A simple TBEND is sufficient. Both TBEND and TBCLOSE flush the in-storage copy of the table, but TBEND doesn't write to disk first. Since it doesn't write to disk, the LIBDEF for ISPTABL is not necessary for TBEND.
<p align=justify>As with other LIBDEFs, this one only has to exist long enough to close the table. After that, it can be (and should be) released immediately.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-7964324414593283182011-11-23T10:32:00.002-05:002022-05-18T12:33:02.600-04:00ISPF Tables — Defining and Building<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables - Defining and Building
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Building an ISPF table is done by the TBCREATE service. The table may or may not have a key or keys. The table may or may not have one or more name (data) fields. It will almost certainly have at least one of either, but 'almost' is not 'absolutely'. In fact, an ISPF table may be TBCREATED with no specifically-named fields at all... but you had better know what in Hell you are doing in that case. Very likely, before doing so, you would very definitely know what you had in mind. The uses for an 'extension-variables-only' table are breathtakingly esoteric.
<pre>
"TBCREATE" table_name "KEYS(" key_field_names ")",
"NAMES(" data_field_names ")" other_table_qualities
"TBCREATE TEMP [ KEYS() NAMES() ] WRITE REPLACE"
</pre>
<p align=justify>To populate a new table row, each of the named fields, whether KEYS or NAMES, is assigned a value (or not — more later) and the TBADD service is called. The row is created with all KEYS and NAMES fields, whether populated or not, plus all extension variables referenced by the SAVE parameter. Extension variables are potentially unique to each row, and are only loaded to the table if specifically requested in the TBADD/TBMOD service call.
<pre>
"TBCREATE ZORK KEYS(INDEX OFFSET) NAMES(PLACE TYPE VALUE)"
index = index + 1
/* OFFSET not populated */
place = left
type = normal
value = total
oper = userid()
time = Date("S") Time("N")
"TBADD ZORK SAVE(OPER TIME)"
</pre>
<p align=justify>This added one row to table ZORK. All fields except OFFSET have values, and there are two extension variables, OPER and TIME, which are not part of the canonical definition. There <u>is</u> a field for OFFSET, but it is <i>null</i>, and here <i>null</i> has a different meaning than it does in REXX. Here, it means: <i>doesn't exist</i>.
<p align=justify>That's it. That's all there is to populating a table. Updates to the table thereafter can be done via TBADD (for new rows) or TBMOD (for replacement rows — or new rows if the key does not presently exist).
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-64293910093627058782011-11-21T14:45:00.002-05:002022-05-18T12:33:14.837-04:00ISPF Tables — Opening<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables -- Opening
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>When using an ISPF table for input, the table library must be available via ISPTLIB. The library containing the table to be used has to be either part of the original ISPTLIB allocation or LIBDEF'd into a preferential position. If LIBDEF'd, the LIBDEF can reference either a DATASET or a LIBRARY. If a LIBRARY, the library itself must have been previously ALLOCATEd under a unique DDname. This latter method is fraught with hazard and I do not recommend it. I recommend instead using the DATASET route as being much less likely to give you an ulcer:
<pre>
"LIBDEF ISPTLIB DATASET ID("isptlib") STACK"
--- use the dataset ---
"LIBDEF ISPTLIB"
</pre>
<p align=justify>Once the dataset is in place, you must open it with TBOPEN... unless it's already open... unless it's not even present. Well, heck, how do we know <u>that</u>? TBSTATS:
<pre>
"LIBDEF ISPTLIB DATASET ID("isptlib") STACK"
"TBSTATS" $tn$ "STATUS1(s1) STATUS2(s2)"
if s1 > 1 then do
say "Table" $tn$ "not available."
zerrsm = "Table" $tn$ "not available."
zerrlm = "Table" $tn$ "not found in the ISPTLIB library chain"
sw.0error_found = "1"
end; else,
if s2 = 1 then do /* table is not open */
"TBOPEN " $tn$ openmode.NOUPDT
if rc > 4 then do
sw.0error_found = 1
zerrsm = "Table did not OPEN"
zerrlm = "Table" $tn$ "cannot be opened due to prior",
"enqueues."
"SETMSG MSG(ISRZ002)"
end
end
else "TBTOP" $tn$
"LIBDEF ISPTLIB"
</pre>
<p align=justify>STATUS1 generally addresses the data-content of the library. STATUS2 references the OPEN-state of the named table, in this case, variable "$tn$". A value of "1" in STATUS1 indicates that the named table exists in ISPTLIB. A value greater than one signals trouble: the table you've named isn't where it can be opened. This certainly constitutes 'an error'.
<p align=justify>A value of "1" in STATUS2 signals that the named table is not presently open or otherwise in use. If this is not the case (the table is already open - STATUS2 returned a value greater than "1") there is no need to re-open the table. ISPF will ignore such a request. With the table presently in a not-open state, a TBOPEN can be issued for the table. The example here shows that the mode of opening is determined by the current setting of variable "NOUPDT" (set in REXXSKEL's TOOLKIT_INIT section). The table will be opened for WRITE if NOUPDT is off, and NOWRITE if it is on.
<p align=justify>A table which is already open need only be TBTOPped to make it ready for our use.
<p align=justify>As soon as the table is determined to be open, the LIBDEF for the ISPTLIB is no longer needed — a full copy of the table now exists within the user's region. It's good practice to immediately drop the LIBDEF the instant it has served its purpose.
<p align=justify>That's it. That's all there is to opening an ISPF table. From here on in until the table is TBCLOSEd or TBENDed, the complete contents are available for your use. Unfortunately, that makes them <u>unavailable</u> for anyone else's use if the table was opened WRITE, so be a good user: get <u>in</u>, get <u>done</u>, get <u>out</u>.
<p align=justify>If you as the programmer discover that some of your users are hogging the table, you may have to take drastic action to prevent that. 'Drastic action' may include TBCLOSEing the table after every update, TBENDing it after every inquiry, and consequently repeatedly TBOPENing it for each new cycle. In between, you will have to maintain a record of what the original looked like when it was first fetched so that you'll know whether it was changed in the meantime. Lots of CPU cycles wasted because of undisciplined users. You may even wind up rewriting the app to eliminate ISPF... but you will have had the experience of prototyping it (easily) in ISPF and will get the benefits Dr. Brooks promised when he said:<blockquote>When planning a program, plan to do it twice. You're <u>going</u> to do it twice; you might as well plan for it.</blockquote>
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-64937537632929368312011-11-19T14:36:00.001-05:002022-05-18T12:33:56.547-04:00SYSVAR<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
SYSVAR
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>I was going to blog about <i>SYSVARS</i>, a routine I wrote which lists all the <i>SYS</i>tem <i>VAR</i>iable information available in MVS when I realized I didn't have an example of the output. Bummer! So I went on the web to see if there was anything like a manual that would describe the output in a way I could use. I found, instead, (really... how could I forget?) <a href="http://planetmvs.com/freeware/index.html">David Alcock's MVS freeware page</a>. Not only does it have screenshots of the output, but his code is probably better than mine, anyway.
<p align=justify>Why have <i>SYSVAR</i> at all since all it does is display stuff you have no control over? When you're writing code and you need a quick reference, nothing beats being able to <u>see</u> the actual values that will be delivered by a SYSVAR call. Alternatively, when your SYSVAR call isn't working exactly the way you thought it would, you can quickly check that what's being returned is (or isn't) what you thought.
<p align=justify>Little utility/demonstrator programs like <i>SYSVAR</i> can save precious time when you don't have much of it to waste — that's 'all the time' for most of us. You could do worse than to collect a toolbox-full of such things.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-59748970851569884042011-11-18T19:27:00.002-05:002022-05-18T12:34:35.559-04:00ISPF Tables<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
ISPF Tables
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>Most of our uses of ISPF tables are very simple. Every once in a while, however, we might bump into a relatively-more-complex situation where the uses are not so simple. Without fail, such times are opportunities for us to learn some very difficult and painful lessons. It's much better to learn them on a blog than while sitting before a terminal pumping code in a vain attempt to make a deadline. The most difficult lesson is this: reading a table row populates any REXX variables that have matching names (and creates them otherwise). This is most dangerous when you have to work with more than one table simultaneously. Let me illustrate:
<pre>
address ISPEXEC
"TBSKIP ORDERS" /* sets vendor, product, date, qty, invoice */
"TBGET INVENT" /* keyed by 'product', sets qty, descrip */
</pre>
<p align=justify>The value for 'qty' from 'ORDERS' has been overlaid by 'qty' from 'INVENT'. To preserve the original 'qty', it has to be copied to a different variable name. Code-writing time is the wrong place to have to suddenly rename variables wholesale. That is why I recommend that every table variable name should have a two-character prefix unique to the particular table:
<pre>
address ISPEXEC
"TBSKIP ORDERS" /* sets orvendr, orprod, ordate, orqty, orinvc */
"TBVCLEAR INVENT"
inprod = orprod
"TBGET INVENT" /* keyed by 'inprod', sets inqty, indesc */
</pre>
<p align=justify>There is now 'orqty' and 'inqty' and they can be compared, perhaps to see whether it's time to reorder.
<p align=justify>Problem solved? Hardly. ISPF tables have more tricks up their sleeve(s). Behold... extension variables.
<p align=justify>Extension variables are unique to each row, and only the programmer exercising care in their naming can prevent clashes such as above. As with ordinary table variables, extension variables also populate REXX variables when the row is read, and there is no warning that a simple TBSKIP may have clobbered other variables... unless the programmer is smart enough to <u>ask</u> if any extension variables were transferred with the rest of the row:
<pre>
"TBSKIP ORDERS SAVENAME(xvars)"
parse var xvars "(" xvars ")"
</pre>
<p align=justify>which lets us know that 'ornotes', 'ormgrnam' and 'ormgrtel' were set without warning. If we were to update that row without also commanding that those three extension variables be included in the update, they would simply disappear, also without warning. It's a quick way to shoot holes in table data. That's not us, thankfully. We're smart enough to:
<pre>
"TBMOD ORDERS SAVE(" xvars ")"
</pre>
<p align=justify>thus preserving the data in those extension variables.
<p align=justify>As with everything else, a few small proof-of-concept routines can be an invaluable learning aid, and (of course) you need to test the logic to death.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-40948314820992371122011-11-17T09:25:00.001-05:002011-11-17T09:27:32.978-05:00Associative arrays<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
Associative arrays
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>In REXX, arrays do not have to be indexed by numbers. They can be indexed by <u>anything</u>... I think. Certainly they can be indexed by names or other non-numeric tokens, and they certainly don't have to have values for every possibility. Les Koehler calls these 'content addressible arrays' because it is data-content which serves as the index-value for the array.
<p align=justify>It's common to see such arrays in matching routines where an array might be initialized to '0' and set to '1' for each of several dozen/hundred/thousand values as they are encountered in the course of processing data. The value of the array-element tells us, in that case, whether or not we have encountered the index token before, or in the case of an actual counter, how many times we have encountered that token. If you are, for instance, trying to eliminate duplicate data in a file and the duplicates might be scattered randomly throughout the data, this is just what the doctor ordered:
<pre>
exists. = 0
do (over the entire input stream)
/* get the next token */
if exists.token then iterate /* discarding this input */
/* process this input data */
exists.token = 1 /* discard all future */
end
</pre>
<p align=justify>This logic causes each token to be processed only the first time it is encountered. All following occurrences are flushed. If, instead, we wished to know <i>how many</i> of each token were present, the code would simply increment a counter each time the token were encountered. However, in such a case, it will be nigh-on impossible to retrieve the counts at the end of processing unless the program has separately kept a list of the tokens it found, because the tokens could be <u>anything</u>... literally.
<p align=justify>WARNING! One aspect of content-addressible arrays has caused me endless grief because I continually forget the lesson so painfully learned the last time it happened. <i>To the extent those index-values are alphabetic, they are exclusively upper-case.</i> That is: if you set a value as
<pre>
fullname.Tom = "Tom Swift"
</pre>
<p align=justify>you must be careful when retrieving it that you specify the index-value as "TOM". Stated another way, you cannot have separate values for 'fullname.Tom' and 'fullname.TOM'. They occupy the same space. Only the last set value will exist, and it will be indexed by 'TOM'. It is a worthwhile 'exercise for the student' to write a small demonstration program to expose this behavior.
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0tag:blogger.com,1999:blog-5512280456703842915.post-6206999648374923012011-11-16T15:08:00.001-05:002011-11-16T15:15:12.714-05:00The Stack<font face="Trebuchet, Verdana, Helvetica, Arial" size="2">
<!--
Almost all HTML formatting tags are allowed, but beware!
All the " " tags will be zapped.
<head> <title> <body> not allowed. line-return is honored, as is multiple spacing
BLOGSPOT requires a headline external to this text, so an <h3> inline will also be displayed giving you 2 headlines.
<head>
<title>
The Stack
</title>
</head>
<body>
<h3>
...
</h3>
-->
<p align=justify>The best part about REXX (after PARSE, of course) is the stack. Knowing how to exploit the stack can save you mountains of trouble. Once you get to appreciate the power of the stack, it opens doors to a world of capabilities. Remember when we used to write line-mode dialogs: prompt then PULL the response?
<pre>
say "Enter the dataset name:"
pull dsn
</pre>
<p align=justify>But you can't do that if there's already something on the stack. You can't do that if there <u>might</u> be something on the stack. Just in case, you should always phrase the above as
<pre>
"NEWSTACK"
say "Enter the dataset name:"
pull dsn
"DELSTACK"
</pre>
<p align=justify>The same is true if you're dealing with a subroutine that returns its answer on the stack (my favorite mode of inter-module communication). Let's say you have a subroutine that does some function for you and returns its result via the stack:
<pre>
"NEWSTACK"
"CKSTATE" volid
pull state
if state = "MULT" then
do queued()
pull state area
parse value volst.0+1 state area with,
zz volst.zz 1 volst.0 .
end
else volst.1 = state "00"
"DELSTACK"
</pre>
<p align=justify>That is, if your subroutine returns more than one value, the first line of the returned stack is "MULT" and all the returned values follow. If there's only one returned value, it's on line number 1.
<p align=justify>The beauty of "NEWSTACK"/"DELSTACK" is that you can protect any material you've squirreled away against damage by other data that also needs to use the stack. This is especially important when that 'other data' comes from the keyboard in response to a prompt.
<p align=justify>Lastly, the use of "NEWSTACK"/"DELSTACK" <u>guarantees</u> that the current stack is empty. If you want to be 100% sure you're getting a response from the keyboard and no place else, "NEWSTACK" is <u>it</u>. When I want to put out multiple pages of data (HELP-text, for instance, or a screenful of output) and wait for the user's permission to go to the next page, I insert this line where I want to wait for the user to finish reading:
<pre>
"NEWSTACK"; pull ; "CLEAR" ; "DELSTACK"
</pre>
<p align=justify>(Your installation may use some other routine to clear the screen. Just substitute your local flavor.)
</font>rexxheadhttp://www.blogger.com/profile/12578166996312186309noreply@blogger.com0