Copyright 1989-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 April, 1989 Brushing Up on Lexicals By Kevin G. Barkes Nothing invokes terror in the heart of a beginning DCL command procedure writer more than lexical functions, those arcane constructs beginning with F$ and frequently ending with a tangle of acronyms. This can probably be attributed to the less-than-friendly sounding moniker to which they're attached. The term "lexical function" sounds vaguely sinister, like the deeds practiced by a super-villian. The truth is actually much more mundane. The functions are so named because the DCL command interpreter processes them during the first, or lexical, phase of command processing. This phase is also referred to as the command input scanning phase. Thus, lexicals could have been called command input scanning functions, so things could have been worse. Lexical functions provide DCL with an impressive array of capabilities. Through calls to various system services, command procedures employing lexicals can return vast quantities of information concerning a system and the processes existing on it, as well as perform manipulations on strings. DEC has added lexical functions to DCL in each major release of VMS. In version 5.0, the f$getqui() function was included, a marvelous lexical which can return literally hundreds of items of information about a system's batch and print queues. Lexicals also provide a sort of "carbon dating" method of determining the length of time a DCL programmer has been working with VMS. Those who, out of habit, use a combination of sys$disk: and f$directory() instead of f$environment("default") immediately flag themselves as dinosaurs from pre-4.0 versions of VMS, as do those who continue using f$logical() instead of f$trnlnm(). But we digress. Lexicals are not as impenetrable as they seem. There are four parts to a lexical function: the f$, which alerts DCL that a lexical is entering the command stream; the name of the function itself; a set of parentheses () which may contain arguments or may be empty; and the argument or arguments for the function. The syntax for calling a lexical from DCL is, in many cases, much simpler than the equivalent system service call in a high-level language. I've seen programmers write code which spawns a DCL subprocess and executes a lexical rather than make a system service call in the "native" language. F$time() is an example of a simple lexical. It returns the system time in the format dd-mm-yyy hh:mm:ss.cc. It has no arguments, but notice the parentheses are still required. Lexicals can be used in the same manner in which symbols are used. It's not necessary to assign the value of a lexical to a symbol prior to its use: $ time = f$time() $ write sys$output "The time is ''time'." is the same as $ write sys$output "The time is ''f$time()'." or $ write sys$output "The time is "+f$time()+"." Since lexicals can be used as if they were symbols, one lexical can serve as one or more arguments to another lexical, such as $ string = "This is a string. " $ write sys$output f$length(f$edit(string,"trim")) 16 In this example, the f$edit function trims the trailing spaces from the text contained in the symbol "string", and f$length returns the size of the remaining text. The rules for using lexicals are simple and clearly stated in DEC's "DCL Concepts" manual: 1. Don't enclose lexicals in quotation marks. When you do, DCL treats the function as a string and doesn't evaluate it. Refer to the f$time() example above for the proper way to use lexicals within a quoted string. 2. Enclose the argument list in parentheses. (See f$edit, above.) 3. If an argument contains a string, enclose it in quotes. 4. If an argument contains a symbol, integer, or other lexical function, don't enclose the value in quotes. Note the following examples, which all produce the same result on output: $ string = " is a test " $ arg = "trim" $ wso = "write sys$output" $ wso "This "+f$edit(string,arg)+"." This is a test. $ wso "This ''f$edit(string,arg)'." This is a test. $ wso "This ''f$edit("is a test ","trim")'." This is a test. You can abbreviate lexical names to their shortest nonambiguous length. f$d(), for example, is a legitimate call to f$directory(). f$e is not legal, since DCL can't determine whether you wish to use f$edit(), f$element(), f$environment() or f$extract(). "Legitimate" calls would be f$ed(), f$el(), f$en() and f$ex(), respectively. Why the sudden interest with lexicals, you may ask? Many of the reader- submitted command files we've been getting recently either use lexicals improperly or forsake them entirely, and we're trying to nudge you in the proper direction. One reader who does exploit the power of lexical functions is Dan "Vern" Hellwig of Aetna Life & Casualty in Hartford, CT. Vern's procedure, DISK.COM (Procedure 1) uses lexicals to their fullest advantage to provide a graphical display of disk usage. The only modification necessary is the device name; Vern's system uses dual- ported disks, hence the "$1" prefix to the "DUA" disk name. Also, the procedure assumes the disks are all of the same type and on the same controller. If your site is different, you'll have to rework the processing loop to accommodate the changes. DISK.COM makes good use of the f$fao() function, a lexical that has always confounded me. FAO, by the way, stands for "formatted ascii output". My pet name for it is f$schwartz(). Pardon the commercial plug. ---------- Kevin G. Barkes is an independent consultant. He publishes the KGB Report newsletter, operates the www.kgbreport.com website, lurks on comp.os.vms, and can be reached at kgbarkes@gmail.com. ******* PROCEDURE 1 $! DISK.COM - Graphic display of disk usage $ v = F$Verify(0) !Disable SET VERIFY $ norm[0,32] = %x6D305B1B !normal rendition $ bold[0,32] = %x6D315B1B !bold rendition $ Write sys$output F$FAO("!20 !50*-") !Output heading line $ unit_number = 0 !Start with unit 0 $loop: !Loop through disks $ device_name = "$1$DUA" + F$String(unit_number) + ":" !Build device name $ If .NOT. F$GetDvI(device_name,"EXISTS") - !If nonexistant device Then Exit 1 + 0*f$verify(v) !exit restoring VERIFY $ unit_number = unit_number + 1 !Increment unit number $ if .NOT. F$GetDvI(device_name,"MNT") - !Skip devices that are Then GoTo loop !not mounted $ logvolnam = F$GetDvI(device_name,"LOGVOLNAM") !Get logical name $ If F$Locate(P1,logvolnam) .eqs. F$Length(logvolnam) - !If P1 (optional) isn't Then GoTo loop !a substring, skip it $ media_name = F$GetDvI(device_name, "MEDIA_NAME") !Get device type $ freeblocks = F$GetDvI(device_name, "FREEBLOCKS") !Get free block count $ maxblock = F$GetDvi(device_name, "MAXBLOCK") !Get maximum blocks $ percent = (maxblock-freeblocks) * 100 / maxblock !Calculate percent used $ If percent .GE. 0 - !Make percent_bar Then percent_bar = F$FAO("!#*|",percent/2) ! $ If percent .LT. 0 - !Known VMS bug, to fix Then percent_bar = bold + "Device error" + norm !disk must be remounted $ If percent .GE. 70 - !If device getting full Then percent_bar = bold + percent_bar + norm !highlight percent_bar $ Write sys$output - !Output detail line F$FAO("!20 !AS!UL%", - ! media_name, logvolnam, percent_bar, percent) ! $ GoTo loop !end loop