Copyright 1994-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! DCL Dialogue Originally published February, 1994 The Belated Return of Stupid DCL Tricks By Kevin G. Barkes *************** Hard to believe, but it's been two years since the last "official" Stupid DCL Tricks (SDT) episode. Bear in mind the code here is not optimized and represents reader examples of quick hacks to do nifty things. There are undoubtedly better, faster, "more elegant" approaches, but that's beside the point. It takes a respectable amount of courage to submit code that will be critically examined by 90,000 or so people. So without further delay... DCL FILTER: There are times when a DCL command issues screens and screens of output when all you want to see is just one or two lines of particular interest. Richard Davis, a senior systems programmer at Home Office Reference Laboratory in Lenexa, KS submitted CSEARCH.COM (PROCEDURE 1), which executes a DCL command and directs the output of that command to a file. The file is then searched for a user-specified string, the string is displayed and the temporary output file is deleted. For example, if you want to see all the symbionts on your system, you'd enter: @csearch "SHO SYS" SYMBIONT and the resultant output would be something like: Searching SHO SYS for SYMBIONT: 00000056 SYMBIONT_1 HIB 4 15 0 00:00:00.08 185 135 I made one minor change to Richard's .com file. I switched a DEFINE/DEASSIGN pair to the simpler DEFINE/USER command. MULTIPLE COMMANDS: Benjamin Bor from London, England, favors us with DO_MANY.COM, a procedure which allows you to put multiple DCL commands on a single line. Each command has to be separated by a vertical bar (|). "I have used it to time long running interactive programs," Benjamin explains, such as: $ @do_many "show proc.acc | run calc_pi | show proc/acc" Perfectionists may want to substitute the newer F$ELEMENT lexical for Benjamin's use of F$EXTRACT and add some additional error handling, but it works just fine for me. ESOTERICA Steve Duff of Ergodic Systems is a string processing, pattern-matching guru of sorts; his company's Macro SPITBOL is about the best tool around for industrial-strength text processing applications. Steve offers an interesting pattern matching procedure in DCL, which can serve as the "engine" of some pretty exotic applications (PROCEDURE 3). DEALING WITH BIG .DIRS: Lurking on Internet, CompuServe and other online systems often pays off. Many times you can find ingenious ways to handle common problems. Take file deletions in extremely large directories: Once a VMS directory file gets above 128 blocks or so in size, it no longer is cached, so all operations get slower. In directories containing thousands of files, it can literally take a minute or more to delete a single file. The reason is due to the design of VMS .DIR files, which are alphabetically sorted sequential files. When you do a wildcard delete in a massive directory, DELETE will kill files in alphabetical order, which means removing the first entry in the .dir file, shuffling all the other entries down one, and so on. Nick de Smith offered a simple solution on CompuServe's VAXForum: create a directory listing and invert it. For example: $ DIR/NOHEAD/NOTRAIL/OUT=KILL.COM creates a directory listing with no leading or trailing text: USER2:[BARKES.PROPRESS.DCL94]BARKES.9401;4 USER2:[BARKES.PROPRESS.DCL94]BARKES.9402;5 USER2:[BARKES.PROPRESS.DCL94]CSEARCH.COM;4 USER2:[BARKES.PROPRESS.DCL94]DO_MANY.COM;4 USER2:[BARKES.PROPRESS.DCL94]KILL.COM;3 Edit the file, remove the reference to kill.com, your edit session's journal file and any files you want to save. Do a global search and replace to create the necesessary DELETE commands. For example, search for USER2: and replace it with $ DELETE/NOCONFIRM/LOG USER2: which gives us: $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]BARKES.9401;4 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]BARKES.9402;5 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]CSEARCH.COM;4 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]DO_MANY.COM;4 Then, sort the file in descending, rather than ascending order: $ SORT KILL.COM KILL.COM /KEY=(POS:1,SIZE:200,DESCEND) Ignore the informational message %SORT-I-KEY_LEN, invalid key length, key number 1, length 200 and open the file KILL.COM, which is now sorted in reverse alphabetical order: $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]DO_MANY.COM;4 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]CSEARCH.COM;4 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]BARKES.9402;5 $ DELETE/NOCONFIRM/LOG USER2:[BARKES.PROPRESS.DCL94]BARKES.9401;4 Execute the file, and the deletion process will go much faster since the system only has to remove the last entry of the .DIR and perhaps truncate the file. PORT SILENCER: As we've noted before, one of the most common problems connecting modems to VAXes is the pointless "conversation" the modem and VMS can have with each other. Let's say a system broadcast message hits the modem port. The modem sees something it doesn't understand, and says "ERROR". The VAX says "Username:" to the modem. The modem says "ERROR". The VAX says "Password:". The modem says "ERROR". The VAX says "User authorization error". The modem says "ERROR". The VAX says "Username:"... well, you get the idea. Depending on how you have system security configured, VMS will think there's a break-in attempt in progress and ignore the port for awhile. The attempted "logins" will cause needless process creations as well. There are a number of ways to avoid this problem, most dealing with modem and port settings. Steve Duff (see above) offered an interesting idea on CompuServe: set a system password on your box's modem ports. "Aside from the security benefit," Steve notes, "it prevents the modem and VAX from chattering at each other, which is usually the case of the status [on the show user display]." This is done with the SET TERMINAL/SYSPASSWORD command, which requires logical i/o (LOG_IO) privilege. When set, VMS requires that the system password be entered before the user sees the Username: prompt. There's no prompt for the system password and no failure message generated, therefore no "extraneous" text to confuse either the modem or VMS. Of course, this solution isn't really practical unless you're dealing with a workstation or a system where all the users have access to the system password. But it's a nifty approach, nonetheless. ON THE WIRE: Readers checking in this month include Thomas Biggi, Dr. Phil Heeler, Stephen Adkins, Mark McDonald, Michael Fleming, Susan Bailey, Armando Valdez, Steve Gibbons, Jim Laferriere, John Younker, Larry Henson, Curt Bemis and John Hinkamp. I generally respond to all messages within a week; if you don't get a reply, assume the letter/email/six-pack was misdirected and try again. ************ EXERCISE IN FUTILITY? As a good little DEC customer, I filled out my Service Quality Review form in official number two pencil the day it arrived. I've filled out lots of them, but so far have never been contacted for follow-up on my comments. So, I thought I'd share with you, gentle readers, my comments on the form. Maybe if we all enter the same questions and comments, Digital will actually respond. As for my suggestion for another question on the questionnaire, I entered: "Do you feel Digital's licensing practices and prices for software and support services are reasonable?" And the comments and suggestions for improvements: 1. Simplify software support contract selection and administration. 2. Have _one_ price for products; list additional support/service items as separate options. 3. Have an ombudsman who can handle all questions/complaints instead of having the customer make numerous phone calls. 4. How can a company which doesn't listen to its customers call itself "customer-driven"? Interesting note: Under its list of software products, Digital calls it "VMS", not "OpenVMS". Hmmm. COKE vs. COCA-COLA. Digital says it wants to be called "Digital", not "DEC". Nothing like squandering brand identity, eh? What about all that DEC-prefixed stuff out there, like DECdirect, DEClease, DECstation, etc.? Wonder if Digital realizes that all digital computers are not Digital computers? Or that "customer driven" does not mean driving customers away with hare-brained marketing gaffes like OpenVMS? ******************** Kevin G. Barkes is an independent consultant whose son turns 18 this month. To Dad, this means more sleep, since he won't have to chaeuffer the kid around like he did when the Cinderella license expired at midnight. To Mom, this means less sleep, since she will stay awake worrying about him. Sigh. C'est la vie. Kevin lurks on comp.os.vms and can be reached at kgbarkes@gmail.com. PROCEDURE 1 $ COMMAND = P1 $ SRCH_STRING = P2 $ TMPFILE = "SYS$SCRATCH:PIPE_''F$GETJPI(0,"PID")'.TMP" $! $ GET_COMMAND: $ IF COMMAND .EQS. "" $ THEN READ/END=EXIT_LABEL/PROMPT="$_Command: " SYS$COMMAND COMMAND $ GOTO GET_COMMAND $ ENDIF $ GET_STRING: $ IF SRCH_STRING .EQS. "" $ THEN READ/END=EXIT_LABEL/PROMPT="$_String: " SYS$COMMAND SRCH_STRING $ GOTO GET_STRING $ ENDIF $ DO_IT: $ SET NOON $ ON CONTROL_Y THEN GOTO CTRL_Y $ DEFINE/USER SYS$OUTPUT 'TMPFILE' $ 'COMMAND' $ ON CONTROL_Y THEN GOTO CLEANUP $ WRITE SYS$OUTPUT "Searching "+COMMAND+" for "+SRCH_STRING+":" $ SEARCH 'TMPFILE' "''SRCH_STRING'" $ CLEANUP: $ IF F$SEARCH(TMPFILE) .NES. "" THEN DELETE/NOLOG 'TMPFILE'; $ EXIT_LABEL: $ EXIT $ CTRL_Y: $ GOTOCLEANUP ***************************************** PROCEDURE 2 $! DO_MANY.COM $! Allows placement of multiple commands on one command $! line. Commands must be separated by a vertical bar $! "|". Example: $! $ DO_MANY "SAY BELL | WAIT 00:00:02 SAY BELL" $! which sounds the bell, waits two seconds, and sounds $! the bell again (assuming, of course, you have $! symbols defined for SAY and BELL. They're $! defined locally here so the procedure works $! with the example: $ SAY = "WRITE SYS$OUTPUT" $ BELL[0,7] = 7 $! Real procedure starts here: $ PARAM = F$EDIT(P1,"COMPRESS,TRIM") $ ON ERROR THEN GOTO ERROR_FOUND $ LOOP: $ IF PARAM .EQS. "" THEN EXIT $ BAR_POSITION = F$LOCATE("|",PARAM) $ COMMAND = F$EXTRACT(0,BAR_POSITION,PARAM) $ REST = F$LENGTH(PARAM) - BAR_POSITION $ PARAM = F$EXTRACT(BAR_POSITION+1,REST,PARAM) $ 'COMMAND' $ GOTO LOOP $ ERROR_FOUND: $ WRITE SYS$OUTPUT F$MESSAGE($STATUS) $ WRITE SYS$OUTPUT "ERROR FOUND, aborting." $ EXIT *********************** PROCEDURE 3: $! @MATCH Pattern String - Match a subject string to a pattern. $! The pattern characters are: $! * - Match zero or more characters $! % - Match any one character $! ^ - Match only if remainder of match fails (eg ^A* matches anything $! not beginning with "A"). $! Returns 1 if match successful, 3 if not. No other outputs. $! **************************************************************************** $! Copyright 1993, ERGODIC SYSTEMS, INC. Lake Forest CA USA 92630 $! Written in VMS V5 DCL - Steve Duff 12/93 $! This code is provided AS-IS, with no warranty of fitness or merchantability $! for any purpose or use, and may not be correct. The recipient alone $! assumes all risk and liability for use of this code. $! Permission is hereby granted to use this code without limitation provided $! that this disclaimer and copyright notice are retained. $! **************************************************************************** $!Create first stack element's vars $ Patstk_Pc_1 = 0 $ Patstk_Sc_1 = 0 $!Canonicalize args $ Pattern = F$edit(P1,"UPCASE,TRIM") $ Subject = F$edit(P2,"UPCASE,TRIM") $!Asterisk pattern matches anything (optimization) $ IF Pattern .Eqs. "*" Then Exit 1 $!SC is the string cursor during the match. SL is the string length $ Sl = F$len(Subject) $ Sc = 1 $!PC is the pattern cursor during the match. PL is the pattern length. $!We leave one character at the end to avoid boundary tests $ Pl = F$len(Pattern) $ Pc = 1 $!STKP is the stack pointer for the stacked alternatives $ Stkp = 1 $!Loop until all stacked possibilities exhausted $Psblty_loop: $ If Stkp .Le. 0 Then Goto Psblty_loop_done $! Assume no stack pop required when done, and that result will be found $ Pop = 0 $ Result = 1 $! If pattern exhausted, then string had better be too $ IF Pc .Gt. Pl $ THEN Pop = 1 $ Result = Sc .Gt. Sl $! Otherwise, case on pattern character $ ELSE $ Charp = F$extr(Pc - 1, 1, Pattern) $ Caseval = F$locate(Charp, "*^%") $ Goto Pchr_'Caseval' $ Pchr_0: !"*" $! If trailing asterisk, match rest of string (optimization) $! Otherwise, match nothing and stack alternative $ IF Pc .Eq. Pl $ THEN Sc = Sl + 1 $ ELSE $ Patstk_Pc_'Stkp' = Pc $ Patstk_Sc_'Stkp' = Sc $ Stkp = Stkp + 1 $ ENDIF $ Goto Pchr_done $ Pchr_1: !"^" $! For complement character, no result, and stack alternative $ Result = 0 $ Patstk_Pc_'Stkp' = Pc $ Patstk_Sc_'Stkp' = Sc $ Stkp = Stkp + 1 $ Goto Pchr_done $ Pchr_2: !"%" $! Cannot match "%" char if string exhausted, otherwise "%" matches $ IF Sc .Gt. Sl $ THEN Result = 0 $ Pop = 1 $ ELSE $ Sc = Sc + 1 $ ENDIF $ Goto Pchr_done $ Pchr_3: !Any non-pattern char $! Cannot match char if string exhausted, otherwise char must match exactly $ IF Sc .Gt. Sl $ THEN Result = 0 $ Pop = 1 $ ELSE $ IF Charp .Eqs. F$extr(Sc - 1, 1, Subject) $ THEN Sc = Sc + 1 $ ELSE $ Result = 0 $ Pop = 1 $ ENDIF $ ENDIF $ Goto Pchr_done $! Here when finished processing one pattern character $! Move to next pattern character. If failure point to top stack element. $ Pchr_done: $ ENDIF $ If Pop Then Stkp = Stkp - 1 $! Handle failure by looping to pop alternatives. $ Pop_loop: $ If (.Not. Pop) .Or. (Stkp .Le. 0) Then Goto Pop_loop_done $ Pc = Patstk_Pc_'Stkp' $ Sc = Patstk_Sc_'Stkp' $! If popped alternative is an asterisk, then continue "*" if no result $! and more chars remain. Otherwise pop past it. $ IF F$extr(Pc - 1, 1, Pattern) .Eqs. "*" $ THEN $ IF (.Not. Result) .And. (Sc .Le. Sl) $ THEN Pop = 0 $ Sc = Sc + 1 $ Patstk_Sc_'Stkp' = Sc $ Stkp = Stkp + 1 $ Pc = Pc + 1 $ ELSE Stkp = Stkp - 1 $ ENDIF $! Otherwise, must be complement alternative. Complement result flag $! and pop next alternative $ ELSE Result = .Not. Result $ Stkp = Stkp - 1 $ ENDIF $ Goto Pop_loop $ Pop_loop_done: $ Goto Psblty_loop $Psblty_loop_done: $! Return final result $ If Result Then Exit 1 $ Exit 3