Copyright 1987-2016 by Kevin G. Barkes All rights reserved. This article may be duplicated or redistributed provided no alterations of any kind are made to this file. This edition of DCL Dialogue is sponsored by Networking Dynamics, developers and marketers of productivity software for OpenVMS systems. Contact our website www.networkingdynamics.com to download free demos of our software and see how you will save time, money and raise productivity! Be sure to mention DCL Dialogue! Originally published May, 1987 DCL DIALOGUE By Kevin G. Barkes Tackling The Mail The mailbag (both physical and electronic) is really stuffed this month, and if I don't start chipping away at it I'll never get caught up. A good deal of your comments dealt with the DCL "stacks" procedures in the January issue. I'm saving those letters for June, when we'll also publish a similar set of procedures for pushing and popping privileges. Intended for use by system managers, these command files should help take the drudgery out of setting and resetting privileges manually. Those of you concerned with symbol space considerations will have a field day with this one. (Here's a trivia question to tide you over... how many characters are there in the string containing all possible VMS privileges? Hold onto your CLISYMTBL values, folk!) One of DCL's major strong points is the variety of approaches available for tackling a problem. In last September's "Quick and Dirty DCL Debugging" column, I suggested using a logical name "switch" to turn command procedure verification off and on. Eric Ross of New York City suggested using a global symbol instead of a logical name as the "switch". Bob De Wolf, a system engineer based in Fullerton, California, developed his own "selective procedure verification" method. He suggests placing entry and exit sequences in all procedures requiring selective verification. A slightly modified version of his original approach follows: $! Entry sequence: $! Save current verification status in $! the symbol SAVE_VERIFY. F$VERIFY(0) $! turns off verification: $ SAVE_VERIFY = F$VERIFY(0) $! $! Make sure the symbol DCL_VFY is $! assigned so we don't generate an $! error: $ IF F$TYPE(DCL_VFY) .EQS. "" THEN - DCL_VFY = "" $! $! Let's assume this .COM file is named $! TEST.COM. See text for explanation: $ IF F$LOCATE("TEST.COM",DCL_VFY) .NE. - F$LENGTH(DCL_VFY) THEN SET VERIFY . . $! Exit sequence: $! EXIT_VERIFY is a "garbage" symbol $! required by F$VERIFY, which sets $! verification to its status at the $! entry point to the procedure. $ EXIT_VERIFY = F$VERIFY(SAVE_VERIFY) The procedure is quite simple. You define the symbol DCL_VFY to contain the names of the files for which you want verification enabled. More than one file can be specified, such as: $ DCL_VFY :== TEST.COM FILE.COM SAMPLE.COM F$LENGTH(DCL_VFY) returns the length of the string in the DCL_VFY symbol. F$LOCATE("filename",DCL_VFY) looks for the starting offset of the string "filename" in DCL_VFY. If F$LOCATE finds the filename, verification will be enabled since the offset will not match the length of DCL_VFY. If the F$LOCATE call fails, the lexical returns the length of DCL_VFY; since that value will match the F$LENGTH call, verification will not be turned on. One drawback to this method is the need to "customize" each file to so the first argument to F$LOCATE is the name of command procedure. If you make a mistake typing the file name, or change the name of the procedure and forget to update the F$LOCATE call, the sequence won't work. You can avoid the need to manually insert the procedure name by substituting the following code in the entry sequence: $! Have the lexical return the current $! procedure name. $ FILE = F$ENVIRONMENT("PROCEDURE") $! $! Extract just the name and file type, $! since the lexical returns the full $! file spec. $ FILE = F$PARSE(FILE,,,"NAME")+F$PARSE(FILE,,,"TYPE") $! $! Perform the test: $ IF F$LOCATE(FILE,DCL_VFY) .NE. F$LENGTH(DCL_VFY) THEN - SET VERIFY You could reduce the three steps above into one line of code for added efficiency; we broke them down here for clarity. ***** Continental readers checked in recently as well. Rudi W. Stange of West Germany provided a copy of a procedure used at his site which notifies users via MAIL of the completion status of batch jobs, in a manner similar to that detailed in the "Communicating With Batch Jobs" column. Based on the content of the mail message, users can then look at detailed log files to determine the success of the batch run. ***** Laurie Maytrott of the Florida Solar Energy Center pointed out using the READ command instead of INQUIRE keeps unwanted data from getting into the RECALL buffer. (See P. Piotrowski's comments in the February DCL Dialogue.) Instead of using: $ INQUIRE/NOPUNC PSWD "Today's password? " use $ READ SYS$COMMAND PSWD /PROMPT="Today's password? " Data entered via READ doesn't go through the DCL command interpreter, so the string symbol assignment operations performed by INQUIRE -- uppercase conversion, space and tab compression, symbol substitution, and insertion into the RECALL buffer -- aren't done. Space limitations prevented the printing of Mr. Piotrowski's complete letter, in which he said he used READ quite frequently. Unfortunately, he needed the functions performed by INQUIRE, so READ wasn't particularly useful. Two more notes on INQUIRE and the RECALL buffer: If you're using a DCL procedure to obtain a password and think you're safe by setting the terminal to NOECHO prior to doing the INQUIRE, sorry. INQUIRE still sticks the data in the RECALL buffer, where it's quite readable. In these instances, use the READ command as described above. You know what they say... INQUIREing minds want to know. While there's no way to clear the RECALL buffer from DCL, Gerald Soo of Shared Medical Systems reported on ARIS that a MACRO program, FLUSH.MAR, is available on one of the 1986 DECUS tapes to perform this function. The program resets the command pointer to the beginning of the buffer, in essence making the commands following it inaccessible. Mr. Soo says the program "works quite well" and is also fast. (Late news... Ms. Maytrott reports FLUSH.MAR is on the DECUS VAX86A tape, in the [.BATTELLE] subdirectory. It was written by Mark Oakley of the Battelle Memorial Institute.) ***** Steven Texin, Systems Group Manager of BSO (Boston Systems Office) raised an important issue which we haven't covered here, namely methods of optimizing DCL code. "I am always very appreciative of the style used in documenting your DCL command files," Steven said. "They are not written, however, for maximum performance under DCL and I wonder if your readers realize that. It is of course quite impossible to write DCL routines that are both self- documenting and execute at maximum efficiency." He's come quickly to the point of a major problem faced by all of us who write "instructional" software. To obtain optimum speed, especially in an interpreted language like DCL, it's necessary to eliminate all comment lines from within the active body of the code, structure the procedures so infrequently-used lines are well out of the main processing loops, use compound statements, and limit the size of symbol names. If this is done, the resulting command procedures are pretty much indecipherable to all but the most advanced readers. Steven's site makes a practice of keeping two versions of command files; one version containing full comments, another stripped down to the bare bones for speedy execution. It's an important point, and I thank him for raising it. ***** $! See description at end of file. $! $! Set error handling: $ SET NOON $! $! If in batch mode, go directly to the mailer: $ IF F$MODE() .EQS. "BATCH" THEN GOTO DOIT $! $! Set control_y interrupt: $ ON CONTROL_Y THEN GOTO ERR_HANDLER $! $! Check command line for parameters 1, 2 & 3 $ IF P1 .NES. "" THEN GOTO GOT_P1 $! $ GET_P1: $! Get the mail delivery time: $ INQUIRE P1 "When" $ IF P1 .EQS. "" THEN GOTO GET_P1 $! $ GOT_P1: $ CHECKTIME = F$CVTIME(P1) $ IF .NOT. $STATUS THEN GOTO GET_P1 $ ON WARNING THEN GOTO ERR_HANDLER $! $ GET_P2: $! Determine the recipient: $ IF P2 .NES. "" THEN GOTO GOT_P2 $ INQUIRE P2 "Who (=self)" $! $ GOT_P2: $! If P2 is a mail distribution file, check for its existence $! and construct a legal MAIL recipient-name string. $ IF F$EXTRACT(0,1,P2) .NES. "@" THEN GOTO CHECK_P3 $ DN = P2 - "@" $ DN = F$PARSE(DN,".DIS") $ IF F$SEARCH(DN) .NES. "" THEN GOTO BUILD_DIS $ WSO "Distribution file ",DN," does not exist." $ P2 = "" $ GOTO GET_P2 $ BUILD_DIS: $ QU = """ $ P2 = QU+"@"+DN+QU $! $ CHECK_P3: $! Will a message be entered on-line or a file used? $ IF P3 .NES. "" THEN GOTO GOT_P3 $! $! Build the name of the file in which the message will be stored. $! $ GET_P3: $ FILETEST = "F" $ TMPDIR = F$TRNLNM("SYS$SCRATCH") + F$GETJPI("","PID") $ RAWFN = F$ELEMENT(1," ",F$TIME()) - ":" - ":" $ FN = TMPDIR + RAWFN $ OPEN/WRITE MFILE 'FN' $ IF P3 .EQS. "" THEN GOTO GET_MESSAGE $ WRITE MFILE P3 $ GOTO END_MESSAGE $! $ GET_MESSAGE: $ LINE_CNT = 1 $ WRITE SYS$OUTPUT "Enter message (up to 15 lines)" $ WRITE SYS$OUTPUT "Enter control-z when completed." $! $! Write message to file: $! $ MESSAGE_LOOP: $ READ/PROMPT="Line ''LINE_CNT': " /END=END_MESSAGE SYS$COMMAND MESSAGE $ WRITE MFILE MESSAGE $ IF LINE_CNT .EQ. 15 THEN GOTO END_MESSAGE $ LINE_CNT = LINE_CNT + 1 $ GOTO MESSAGE_LOOP $! $ GOT_P3: $! Exit if the message file does not exist... $ ETEST = F$SEARCH(F$PARSE(P3)) $ IF ETEST .EQS. "" THEN - WRITE SYS$OUTPUT P3," does not exist." $ IF ETEST .EQS. "" THEN EXIT $ FN = F$PARSE(P3) ! otherwise, use P3 for the message $ FILETEST = "T" $! $ END_MESSAGE: $ CLOSE MFILE $! $! Queue message request: $! $ IF P2 .EQS. "" THEN P2 = F$EDIT(F$GETJPI("","USERNAME"),"TRIM") $ SUBMIT/NOPRINT/NOLOG/PAR=('FN','P2','FILETEST') - /AFTER="''P1'" 'F$ENVIRONMENT("PROCEDURE") $ EXIT $! $ ERR_HANDLER: $ WRITE SYS$OUTPUT "Unable to queue message request..." $ SAVE_MESSAGE = F$ENVIRONMENT("MESSAGE") $ SET MESSAGE/NOF/NOI/NOS/NOT $ CLOSE MFILE $ SET MESSAGE 'SAVE_MESSAGE' $! $! Delete the file FN created by this routine _only, DO NOT delete $! any existing files if P3 was a file specification. $! $ IF FN .NES. "" .AND. P3 .EQS. "" THEN DELETE/NOCONFIRM 'F$PARSE(FN)'0 $ EXIT $! $! Process message request $! $ DOIT: $ MAIL/SUBJECT="AutoMessage" 'P1' "''P2'" $ IF P3 THEN EXIT $ DELETE/NOCONFIRM 'F$PARSE(P1)'* $ EXIT $! $! Procedure info: $! $! message.com $! $! Original procedure (alarm.com) by H. Robanske (11/15/86) $! U.S. West Network Systems, Inc., Bellevue, Washington $! $! Modified by C.W. Dickinson (11/19/87) $! GE Consulting Services, Syracuse, New York $! $! MAIL distribution file support and minor changes $! by Kevin G. Barkes (2/13/88) $! $! synopsis: send a message (up to 15 lines) or a file to $! any user or legal mail distribution list (@MAIL.DIS) $! (defaulted to user invoking the procedure), scheduled $! for delivery at a specific time. The message, if $! entered on-line, is ended by typing (control-z). $! $! syntax: @message [