CONDITION('D') returns nothing

I'm trying to perform a file operation. "Filename" is deliberately not set to an existing file, so I should be getting the NOTREADY condition raised. But CONDITION('D') is giving me an empty string (ie, not the name of the file in error).

DO
  STREAM(filename, 'C', 'OPEN READ')
  CATCH NOTREADY
     file_error()
     RETURN
END
RETURN

file_error:
   SAY CONDITION('D')
   RETURN

Answer

The real problem is that the data that CONDITION() can return is temporary data that can be "freed" whenever you enter a new level. This is particular true for file errors since Reginald attempts to clean up some file-related errors as soon as it deems it safe to do so. If you call the CONDITION function right inside of your CATCH NOTREADY block, you'll find the results you expect.

DO
  STREAM(filename, 'C', 'OPEN READ')
  CATCH NOTREADY
    SAY CONDITION('D')
    RETURN
END
RETURN


Error handling

In a main window layout script, there's the CATCH SYNTAX (in the message loop). It calls CONDITION('M') to show an error message. I would like to be able to "jump" there if anything fails in the rest of my code. In a subroutine in the same script, I tried to add something similar to the following:

DO
   LINEOUT("somefilename.txt", "some data")
   CATCH NOTREADY
      RAISE SYNTAX 48
END

If I get a NOTREADY condition (on the LINEOUT function), this jumps to my CATCH NOTREADY. There, I raise a syntax condition which "jumps" to the CATCH SYNTAX in the main window loop. Is this doable, necessary, etc?

Answer

I don't see the point of raising SYNTAX from within a CATCH NOTREADY. The CONDITION() built-in function supports all conditions. That means you can do a CONDITION('M') in your CATCH NOTREADY (and you'll get a much more descriptive message of what the error really is).

So, just do:

CATCH NOTREADY
   CONDITION('M')
If you want to handle all NOTREADY error reporting in one place, in your main window loop, then just put the CATCH NOTREADY there:
/* The end of the standard main window loop... */
    END /* SELECT GuiObject */

  END /* Some child script signaled us */

  CATCH SYNTAX
    CONDITION('M')
    SIGNAL again

  CATCH NOTREADY
    CONDITION('M')
    SIGNAL again

  CATCH HALT
  FINALLY
    GuiDestroyWindow()
END

The only thing is, remember not to leave files open. Put a DO/END around sections of code where you open files, and put a FINALLY section there to close those files. Then when the NOTREADY happens, the FINALLY will get done, and then Reginald will abort all of your child scripts/loops to go back to the main script and display the error message for NOTREADY. (Of course, this assumes that one of your child scripts doesn't intercept that NOTREADY with its own CATCH NOTREADY).


Suppress SAY's line feed

Is there a way to suppress the line feed in the SAY instruction? The reason is to make a loop print its output on the same line rather than a new line:

DO i= 1 TO 3
   SAY i
END

...results in...

1
2
3

what I want is the result:

1 2 3

Answer

The way to do this is to use the CHAROUT function, and omit the filename. Then use a lone SAY to "end" the line:

DO i= 1 TO 3
   CHAROUT(, i || ' ')
END
SAY


One line comment

I'm so used to C++ one line comment i.e. "//". Could Reginald add this?

Answer

// is used to indicate that you want to divide 2 numbers and get the remainder. So, it can't be used as a comment.

Reginald does allow an option you can turn on as so:

OPTIONS "SEMICOMMENT"

This allows everything after a semi-colon, up to the end of line, to be considered a comment. For more details, see the REXX Reference page Miscellaneous topics -> OPTIONS instruction (optional features).


Custom initialization file

I don't want touch the windows registry to save my script's settings, so is it possible to set up a "local" registry in the reginald directory, and use the VALUE() function to access it?

Answer

The VALUE function (with a WIN32 environment arg) works upon only the registry.

But you could easily write your own initialization file feature, taking advantage of the INTERPRET instruction, and LOADTEXT. For example, if you want to save the state of the variables named MyWindowWidth and MyWindowHeight, then just write out a file containing some REXX assignment instructions as so:

LINEOUT("C:\MyInitFile", "MyWindowWidth=" mywindowwidth)
LINEOUT("C:\MyInitFile", "MyWindowHeight=" mywindowheight)

Then at the start of your script, you could check for the existance of that file and read it in with LOADTEXT, and then INTERPRET each line. The net result is that you'll do those variable assignments (and any other instructions you've written to the ini file).

DO
   LOADTEXT("MyLines.", "C:\MyInitFile")
   DO i = 1 TO mylines.0
      INTERPRET mylines.i
   END
   CATCH NOTREADY
END

But note that the reason why Microsoft created the registry is so that programs could transparently support multiple user settings. The operating system takes care of automatically reading/writing data from/to the current user's account.

At the very least, you should do the same thing by saving your file to some directory that is under the current user's account. You could find such a directory, and create your ini filename, using something like:

filename = SEARCHPATH('%DOCS%') || 'MyIniFile'