Showing posts with label Toolbox. Show all posts
Showing posts with label Toolbox. Show all posts

Monday, September 19, 2022

Breathing Room

 

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.

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'.

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.

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.

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...

I have put 'FIXTOOL' out on my alternate source code page, http://frankclarke.dx.am/REXX/listindx.html, and now invite all and sundry to steal a copy and run it through its paces, reporting any errors back to me at my main email address.  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.

  • 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 WRITE to its table.
  • Routine ICEUSER should be downloaded and customized to identify the 'special' users who are expected to manage the tools. 
  • 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. 
  • You, 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.

So, here is the component list of all the software that must be in place in order to run FIXTOOL: 

  • DFLTTLIB
  • FCCMDUPD
  • FIXTOOL
  • ICEUSER
  • LA
  • TBLGEN
  • TBLMSTR
  • TRAPOUT

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.

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.

 

Saturday, November 19, 2011

SYSVAR

I was going to blog about SYSVARS, a routine I wrote which lists all the SYStem VARiable 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?) David Alcock's MVS freeware page.  Not only does it have screenshots of the output, but his code is probably better than mine, anyway.

Why have SYSVAR 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 see 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.

Little utility/demonstrator programs like SYSVAR 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.

Saturday, November 12, 2011

How to empty a sequential dataset

If site security rules prevent you deleting and recreating a sequential dataset but you can write to it (in ACF2 terms, you are "READ(A) WRITE(A) ALLOC(P)"), you can still clear it of records.

"ALLOC FI($TMP) DA($TMP) SHR REU"
"EXECIO 0 DISKW $TMP (OPEN FINIS"
"FREE  FI($TMP)"

I'm about to run out of quick tips.  They'll either get longer and more elaborate (and thus lots less 'quick') or they're going to become fewer and not quite so 'daily'...

Thursday, November 10, 2011

Is it a Leap Year?

Well, really, the only reason we care is to know whether February has 28 days or 29 days, right?  Everything else is the same, because knowing that fact also tells us whether the year is 365 days long or 366.  So, the problem devolves to simply knowing the number of days in February, and that depends strictly upon the question 'what year is it?'.

   days.   = 31                        /* jan mar may jul aug oct dec*/
   days.04 = 30                        /* apr                        */
   days.06 = 30                        /* jun                        */
   days.09 = 30                        /* sep                        */
   days.11 = 30                        /* nov                        */
   days.02 = 28 + (ccyy//4=0) - (ccyy//100=0) + (ccyy//400=0)

(Presuming you have a 4-digit year available for the calculation)  the number of days in February is 28 plus...

One if the year is evenly divisible by 4  (ccyy//4 = 0),  unless...

The year is also evenly divisble by 100  (ccyy//100 = 0)  unless...

The year is also evenly divisble by 400  (ccyy//400 = 0)

Couldn't be simpler.  2000 was a leap year, and February had 28 + 1 - 1 + 1 (=29) days.  1900 was not a leap year;  it had 28 + 1 - 1 (=28) days.

Saturday, November 5, 2011

Masking and Matching

When you have to find things in a list that match things in another list, that's fairly easy.  You simply spin through one list (the shorter one) and for each element there, you see if "WordPos" delivers a match in the other list.  WORDPOS' processing is probably faster by orders of magnitude than anything you can code in REXX using the Bigger Hammer method.

What do you do when you have to find elements in one list that are merely like elements in another list?  Say you need to find all the membernames in a PDS that look like 'BRT**4'.  That's a different problem.  Yes, you could just go get a bigger hammer, or you can reach for the jeweler's screwdrivers.

REXX has two built-in functions, BITOR and BITAND, that can double-team that problem.  BITAND operates on two strings by ANDing them at the bit-level.  BITOR operates on two strings by ORing them at the bit-level.

BITAND returns a 1-bit in every bit position for which both strings have a 1-bit:  BITAND( '73'x , '27'x ) yields '23'x.  '0111 0011' & '0010 0111' = '0010 0011'.

BITOR returns a 1-bit in every bit position for which either string has a 1-bit:  BITOR( '15'x , '24'x ) yields '35'x.  '0001 0101' | '0010 0100' = '0011 0101'.

If there are characters for which it doesn't matter whether they match, those characters can be BITANDed with 'FF'x and BITORed with '00'x.  The result in each case is that the "I don't care" characters are returned intact by the BITAND/BITOR.  For the other characters, the ones for which it does matter whether they match, only an exact match will deliver the proper bit-pattern when they are both BITANDed and BITORed.  Like this:

      memmask = Strip(memmask,"T","*")
      maskl   = Length(memmask)
      lomask  = Translate(memmask, '00'x , "*")
      himask  = Translate(memmask, 'FF'x , "*")
      do Words(mbrlist)                /* each membername            */
         parse var mbrlist mbr mbrlist
         if BitAnd(himask,Left(mbr,maskl)) = ,
             BitOr(lomask,Left(mbr,maskl)) then,
            mbrlist = mbrlist mbr
      end                              /* words                      */

In this case, we have been given 'memmask' which may look like 'BRT**4'.  MASKL is '6'.  LOMASK gets '00'x characters in place of the asterisks.  HIMASK gets 'FF'x characters in place of the asterisks.  We spin through the list of membernames stored in MBRLIST.  For each word ("MBR") in that list, we BITAND it against the HIMASK and BITOR it against the LOMASK.  When the comparison is exactly equal, we have a match.  In this case, we merely attach the matched membername at the back of MBRLIST.  When we have iterated this loop "Words(mbrlist)" times (once for each original word), MBRLIST will contain only those membernames matching "BTR**4".

As with all 'tips and tricks', this is merely one way to 'skin the cat'.

Tuesday, November 1, 2011

The Zenith of Tracing

Zenith used to make TVs, and they may still; I haven't shopped for a TV in years.  Back in The Good Old Days (tm), Zenith had an advertising slogan:  "The quality is built in, not bolted on".  The implication, for those who could read between the lines, is that quality can't be either forgotten at the factory or removed at a later date.  I have the same attitude when it comes to tracing and debugging.

How many times have you seen this in a REXX program:

/* REXX   --  a program to do some stuff.   */
/*  Trace("R")   */
How many times have you written something like that?

This is an invitation to maintainers to de-comment that line when they need to trace the flow of the program.  This is "bolting quality on", not "building it in", and there are a host of problems that it generates.

First, de-commenting that line constitutes "changing the program".  Yes, it's a small change and (I admit) unlikely to have any noticeable effects beyond the fact that the program now runs in trace-mode.  The (philosophical) point here is that you are not really running the program that failed, you're running a program that was changed after it failed.

Then there are other, less philosophical and much more serious, problems:  are you changing the production copy or something else?  If you're changing the production copy, will everyone running this "in production" suddenly find themselves accidentally (and surprisingly) producing reams of output?  If it's not the production copy, are you sure it was an exact duplicate of the version that failed?  Where is your test copy stored?  Is the concatenation sequence the same for your test copy as for the failing version?  Are you sure?  What about called subroutines?  Will your version pick up true copies of all necessary subroutines?  After all, the failure might have been in a subroutine and caused by bad data being passed back to the caller.

So many problems.  One solution.  Build the trace capability in, don't bolt it on.  As with oh-so-many-things, there is more than one way to skin this cat.  All that's necessary is to make it clear, and to do it consistently, and to design it so that the program doesn't have to be changed in order to start a TRACE.  Did I just say "parm"?  Yes, I believe I did.

/* REXX    -- a program to do some stuff
*/ arg argline
address TSO
arg parms "((" opts            /* 'opts' are rightward of a double-open-paren */
parse var opts "TRACE"   tv  . /* looking for "TRACE ?r" or similar           */
parse value tv "O" with tv .   /* if tv is empty, it becomes 'O'              */
rc     = trace(tv)             /* set whatever trace-value was loaded         */
You now can turn on TRACE with any form of TRACE without any change to the program.  You will be running the program that failed, not a copy of it.  Instead of calling it as
TSO BLAH43M for Monday using custfile4 crossfoot subtotals
you issue instead
TSO BLAH43M for Monday using custfile4 crossfoot subtotals (( trace ?r

You can, of course, get much fancier than this, but it's not strictly necessary unless you have an articulable reason.  I do get fancier than this (see REXXSKEL on my REXX page) because much of the parsing code I use is pre-packaged and thus doesn't have to be coded for every routine I write.  Whether you choose 'simple' or 'elaborate', it really doesn't matter.  Just no more de-commenting TRACE commands, okay?  Please?