Operating System - OpenVMS
cancel
Showing results for 
Search instead for 
Did you mean: 

How to clear delayed terminal EOF from DCL

Jess Goodman
Esteemed Contributor

How to clear delayed terminal EOF from DCL

I am having a subtle problem with a complex DCL procedure, that I have boiled down to a simple example below.

Basically the procedure has multiple stages that inputs data interactively using READ SYS$COMMAND. If EOF is reached (via input) during one stage, the procedure moves on to the next stage. However that is not the only condition that will end a stage.

My problem occurs if the user enters a string of data that will, on its own, end one stage but uses instead of to end the input string.

The VMS terminal driver delays signalling an EOF condition for a READ if any data was read before the EOF occurred. Then on the NEXT read to the same channel EOF is signalled immediately.

So in the below example although the EOF actually occurs on the third TREE read, it is not reported until the first FLOWER read.

$ MAX_TREES = 3
$ MAX_FLOWERS = 3
$!
$ I = 0
$TREE_LOOP:
$ READ/END=END_TREE_LOOP/PROMPT="Tree: " SYS$COMMAND STRING
$ I = I+1
$ TREE_'I' = STRING
$ IF (I .LT. MAX_TREES) THEN GOTO TREE_LOOP
$END_TREE_LOOP:
$ TREE_COUNT = I
$!
$ I = 0
$FLOWER_LOOP:
$ READ/END=END_FLOWER_LOOP/PROMPT="Flower: " SYS$COMMAND STRING
$ I = I+1
$ FLOWER_'I' = STRING
$ IF (I .LT. MAX_FLOWERS) THEN GOTO FLOWER_LOOP
$END_FLOWER_LOOP:
$ FLOWER_COUNT = I
$!
$ SHOW SYMBOL TREE_COUNT
$ SHOW SYMBOL FLOWER_COUNT
$!...
$ EXIT

$ @test
Tree: OAK
Tree: ELM
Tree: MAPLE
TREE_COUNT = 3 Hex = 00000003
FLOWER_COUNT = 0 Hex = 00000000

So how do I detect that the EOF did not actually occur in the first FLOWER read? Is there any command that will "clear" this delayed EOF terminal driver condition? I tried "$ CLOSE SYS$INPUT" but that had no effect.
I have one, but it's personal.
9 REPLIES
Doug Phillips
Trusted Contributor

Re: How to clear delayed terminal EOF from DCL

Jess,

I don't know off-hand how to clear typeahead from DCL but I'm sure someone will tell you (us).

But:
If the first read took the /END branch, then the MAPLE input would not be processed. When I do interactive reads in a loop, I prefer not to use ^Z. For ending an input loop prior to max (where any entry might be vaild), I use Just [ENTER], and test for string .eqs. "".

Something like this:

$ set noon
$ MAX_TREES = 3
$!
$ I = 0
$ WRITE SYS$OUTPUT-
"Enter up to ''MAX_TREES' trees. Just press [ENTER] if finished with fewer."
$TREE_LOOP:
$ num = I+1
$ask_tree:
$ READ/end=ask_tree/PROMPT="Tree''num': " -
SYS$COMMAND STRING
$ IF STRING .EQS. "" THEN GOTO END_TREE_LOOP
$ TREE_'I' = STRING
$ I = num
$ IF (I .LT. MAX_TREES) THEN GOTO TREE_LOOP
$END_TREE_LOOP:
$ TREE_COUNT = I
Jess Goodman
Esteemed Contributor

Re: How to clear delayed terminal EOF from DCL

Doug,

Thanks for the feedback. Unfortunately. as I said, the real command procedure is much more complex and I can't use a null input string as the test of end-of-input because it is actually a valid input string for some stages.

In any case, if I could change the user behaviour I would just have them refrain from entering after typing any data on the line. However I must make the procedure bullet-proof no matter what the user types.

I did find two solutions on my own, but I'm not especially fond of either.

* Any image activation clears the delayed EOF condition, so just RUNing a dummy program between each stage will work. (I actually discovered this when I added a SHOW LOGICAL SYS$COMMAND: to the procedure - for a while I thought it was that specific command that did it, until I remembered that SHOW LOGICAL runs an image).

* I can add an intial OPEN INPUT SYS$COMMAND, then use READ INPUT instead of READ SYS$COMMAND. Between each stage I have to CLOSE INPUT and re-OPEN INPUT SYS$COMMAND.

Any other ideas?
I have one, but it's personal.
Steven Schweda
Honored Contributor

Re: How to clear delayed terminal EOF from DCL

It has some effect on the appearance, but you
could try another certain-to-fail READ after
the first one succeeds, so:

$ read /error = end_err1 /prompt = "" -
/time_out = 0 sys$command waste
$ end_err1:

That seems to absorb the EOF, but you get an
extra blank line after normal input.

I don't like it, either. I suspect that
there are countless inelegant ways to get
comparable results. Perhaps some other error
contition (with messages disabled)?

Just one more reason not to use DCL for
everything?
Karl Rohwedder
Honored Contributor

Re: How to clear delayed terminal EOF from DCL

Try inserting a SET TERM/XON after each read.

reagrds Kalle
Karl Rohwedder
Honored Contributor

Re: How to clear delayed terminal EOF from DCL

Pls. ignore my last replay, the SET just runs an image, as you already noticed...

regards Kalle
Doug Phillips
Trusted Contributor

Re: How to clear delayed terminal EOF from DCL

Some other DCL commands, like SET, run images, so maybe you could find an appropriate one to use.

open input sys$command also seems reasonable if you must use DCL and there's absolutely no invalid string.

If the routine is that complex and *must* be bullet proof, it seems like a higher level language would be the better choice (as Mr. Schweda hinted).
John Gillings
Honored Contributor

Re: How to clear delayed terminal EOF from DCL

Jess,

First thing you need to do is abstract out your "Get Input" operation. This is the best way to ensure consistency in your interface, and to deal with issues like this EOF terminator behaviour.

Code it as a GOSUB or CALL procedure. I was hoping an internal CALL would also clear the pending EOF, but it doesn't :-(

Tradeoffs are, CALL is easy to pass parameters in (they become P1, P2, etc...), but returning values must be via global symbols because each CALL level has its own symbol table.

On the other hand, GOSUB is easy to return values, because it shares local symbol table with the caller, but passing values in needs to have symbols set, rather than inline parameters.

I generally opt for GOSUB, rather than use global symbols. So, something like this:

$ InPrompt="Flower: "
$ InSymbol="STRING"
$ GOSUB GetInput
$ IF InEOF THEN GOTO EOFHandler
...

$ GetInput:
$ InLine=""
$ ! do the read with whatever logic you want
...
$ InEOF="FALSE"
$ 'InSymbol'=InLine
$ RETURN
$ InEOFDetected:
$ InEOF="TRUE"
$ RETURN

The important thing is to code your routine so it performs the function exactly how you want. Note that you can easily implement things like context dependent help. From your sample code, you may have an even higher level abstraction - reading an array? If so code that as another layer.


If the only way to kill the pending EOF is to run an image, try this one, hidden in your input routine, rather than clouding up your application logic.

NULL.MAR
.PSECT $CODE,RD,NOWRT,EXE
.ENTRY start,^M<>
$EXIT_S #1
.END start

$ MACRO NULL
$ LINK NULL

(since it's MACRO32, there are no RTLs or other baggage to be activated). Make a copy of the image called "CLEAR_EOF" and run it just before your READ.
A crucible of informative mistakes
Jess Goodman
Esteemed Contributor

Re: How to clear delayed terminal EOF from DCL

Thanks to all who replied. I have three options I can use, although all have minor down sides - or four if you count just ignoring the issue of at the end of data. But recoding the whole application as an executable image is not a good option for us because one of its purposes is to gather input to use for running one or more images.

Doug, I would avoid code like:
$ask_tree:
$ READ/end=ask_tree/PROMPT="Tree:" SYS$COMMAND STRING
because if SYS$COMMAND was redefined to a file or NL: you could have an infinite loop.

John, Thanks for that macro code - is it copyrighted? :)

But at the risk of taking this off topic I would disagree that using a CALL or GOSUB routine as a wrapper for a DCL READ statement improves code by making it more "abstract" or otherwise. IMO the extra lines make the code harder to follow. I would say that DCL already has a built-in "abstract" way to gather input - its READ "object".
I have one, but it's personal.
Doug Phillips
Trusted Contributor

Re: How to clear delayed terminal EOF from DCL

> Doug, I would avoid code like:
> $ask_tree:
> $ READ/end=ask_tree/PROMPT="Tree:" SYS$COMMAND STRING
> because if SYS$COMMAND was redefined to a file
> or NL: you could have an infinite loop.

Seems so, and I could make it so, but not in the context of the example provided. If sys$command were redefined to something that isn't normally open, you must open it before you can read from it. That changes the problem.

$ define/user sys$command nl:
$ask_tree:
$ READ/end=ask_tree/PROMPT="Tree1: "-
SYS$COMMAND STRING
%DCL-W-UNDFIL, file has not been opened by DCL - check logical name

Okay, a better solution would be:

$ endsw = 0
$ask_tree:
$ endsw = endsw + 1
$ if endsw .gt. 1 then goto end_tree_loop
$ READ/end=ask_tree/PROMPT="Tree1: "-
SYS$COMMAND STRING
$...

Another way would be to set a checkpoint value and error-branch to an out-of-line routine.

Anyway, if sys$command is being redefined to NL:, or to a file, other than in /user_mode, then there are major system design problems that need attention. Besides, every non-Q&D command file should do standard housekeeping before executing its working logic.

My simple example (like your simple example) wasn't meant to be comprehensive.

Finally, "gather[ing] input to use for running one or more images" is exactly what a whole bunch of the compiled programs I've ever written do.

(I don't think negative points assignment is possible, is it? ;-)