Operating System - OpenVMS
1748275 Members
3815 Online
108761 Solutions
New Discussion

Re: If interested with this type of simple solution when dealing with VMS logicals

 
Ph Vouters
Valued Contributor

If interested with this type of simple solution when dealing with VMS logicals

Dear all,

 

The DCL solution proposed at http://vouters.dyndns.org/tima/OpenVMS-Java-CRTL-Cleanly_dealing_with_JAVA_and_DECC_logicals-A_proposed_solution.html  might raise some VMS developers interest to confine logicals transparent settings to one application usage without possibly negatively impacting other applications later on executed.

 

This idea is Powell Hazzard's idea when he implemented ANT.COM. The solution here prposed is a complete redesign very looking to me as apparently a trivial implementation. This kind of solution may as well be extented to the global DCL symbols a DCL script needs to define and internally use. In such case you  would use the f$type(symbol) lexicals function..

 

The intent of such type of  solution is to provide a totally logicals "waterproof" solution completely transparent to the end-user whichever he prior or post does. This is only unless he prior defines logicals which negatively impact the solution being executed using this type of solution.

 

A very first "customer" of this type of solution is JRuby/VMS where it is on its way to be fully implemented and will shortly be avaialble  for download.

 

In the very hope this Web document reveals itself useful to someone.

 

Thank for your attention.

Philippe

13 REPLIES 13
John Gillings
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

Phillipe,

 

   Maybe I don't understand the whole issue here. If the operation you want to perform is running an image - like the Java run time environment executing your class file, why doesn't this work?:

 

$ DEFINE/USER DECC$EFS_CHARSET           ENABLED
$ DEFINE/USER DECC$READDIR_DROPDOTNOTYPE TRUE
$ DEFINE/USER DECC$EFS_CASE_PRESERVE     TRUE
$ DEFINE/USER JAVA$DELETE_ALL_VERSIONS   TRUE
$!
$! perform your command requiring the logicals you have specifically set

If I understand correctly, your circumstance is exactly what DEFINE/USER is for. There won't be any USER mode logical names defined, as they're automatically deleted after any image exit. As long as you don't run any images between the DEFINE commands and your Java program, they should be seen and obeyed. The USER mode logical names are then deleted when the image exits, revealing whatever (if anything) was defined before.

 

That said, some comments about your DCL

 

 I don't understand why you need loop1 and loop2? Why can't you (re)define the logical name in loop1?

 

 I think this is a typo: f$trnlnm("''logical_name'","PROCESS") should be F$TRNLNM(logical_name,"LNM$PROCESS")

 

If /USER doesn't help, since I don't like duplicating code, I'd prefer a more general approach which would minimise the amount of code at the point of use. Consider this pair of simple procedures implementing logical name "stacks" stored in global symbols.

 

$ ! DEFINEPUSH.COM
$ !  Save current value of a logical name and define a new one
$ ! p1 = logical name
$ ! p2 = new value (optional)
$
$  IF p1.EQS."" THEN EXIT
$  IF F$TYPE('p1'_N).EQS."" THEN 'p1'_N==0
$  'p1'_N=='p1'_N+1
$  i='p1'_N
$  'p1''i'==F$TRNLNM(p1,"LNM$PROCESS")
$  IF 'p1''i'.NES."" THEN DEASSIGN 'p1'
$  IF p2.NES."" THEN DEFINE/NOLOG 'p1' 'p2'
$ EXIT

$! DEFINEPOP.COM
$!  Restore previous logical name value (if it exists)
$! p1 = logical name
$
$ IF p1.EQS."" THEN EXIT
$ IF F$TRNLNM(p1).NES."" THEN DEASSIGN 'p1'
$ IF F$TYPE('p1'_N).EQS."" THEN EXIT
$ i='p1'_N
$ IF i.LE.0 THEN EXIT
$ 'p1'_N=='p1'_n-1
$ IF F$TYPE('p1''i').EQS."" THEN EXIT
$ IF 'p1''i'.NES."" THEN DEFINE 'p1' &'p1''i'
$ DELETEX/SYMBOL/GLOBAL 'p1''i'
$ EXIT

 

Using these procedures, your example would be coded as:

 

$ @DEFINEPUSH DECC$EFS_CHARSET           ENABLED
$ @DEFINEPUSH DECC$READDIR_DROPDOTNOTYPE TRUE
$ @DEFINEPUSH DECC$EFS_CASE_PRESERVE     TRUE
$ @DEFINEPUSH JAVA$DELETE_ALL_VERSIONS   TRUE
$! 
$! perform your command requiring the logicals you have specifically set
$!
$ @DEFINEPOP DECC$EFS_CHARSET
$ @DEFINEPOP DECC$READDIR_DROPDOTNOTYPE
$ @DEFINEPOP DECC$EFS_CASE_PRESERVE
$ @DEFINEPOP JAVA$DELETE_ALL_VERSIONS

 

A crucible of informative mistakes
Hoff
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

I wonder whether this application image feature-provisioning and image-signing swamp will get drained.  Some sort of an application-provisioning tool and related mechanisms within VMS and the underlying libraries, and particularly a mechanism for bypassing these pesky feature logical names.  And for allowing individual applications to be digitally signed, too.

John Gillings
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

Hoff,

 

>Some sort of an application-provisioning tool and related mechanisms

 

  What? Someone actually implement a new feature in DCL? Hah! In your dreams!

 

I'm having enough trouble getting simple bugs fixed. Here's my latest. It's a V1.0 bug in time parsing which affects ALL DCL commands which have a time related qualifier /AFTER, /BEFORE etc... I found a code path in the T4$COLLECT.COM procedure which leaves excess spaces on time strings passed to the SUBMIT command. The result is a syntax error for strings which end in 06 or higher, but a silent misinterpretation error for strings ending in 01, 02, 03, 04 or 05. This is trivially simple to reproduce:

 

$ submit/after="15:05 " login.com
Job LOGIN (queue SYS$BATCH, entry 748) holding until 13-OCT-2011 15:50

 Note the time is 15:50, not 15:05 as requested.

 

I've provided VMS engineering with a diagnosis, (the trailing blanks become zero, so "15:050"), identified the module and line number where the error occurs (LIB$CVT_ATIME), and proposed the simple solution of stripping trailing whitespace in the input string. Just a few instructions. Low risk and a whole class of potential bugs in numerous commands and countless command procedures are eliminated.

 

The response from VMS engineering?

 

Engineering further analyzed the problem and found that the specifying the trailing space in the absolute time specification is not a recommended format or use case. The formats for specifying the absolute time ("dd-mmm-yyyy hh:mm:ss.cc") is provided in the online help for Date_Time. Please refer online help on DATE_TIME for more details. We request the customer not to use the trailing space while specifying the absolute time. 

 As far as I'm concerned, it's either illegal syntax, in which case it should generate an error, or it should be accepted. The current behaviour of silently using an incorrect time is very un-VMSish. Compare with:

$ submit/after="15:06 "
%DCL-W-IVATIME, invalid absolute time - use DD-MMM-YYYY:HH:MM:SS.CC format
 \15:06 \

 Never mind that it's not ME who's using it, it's in T4$COLLECT, provided by OpenVMS engineering! For that matter, this is the least worst bug I've identified in T4$COLLECT. There are several that I can't get fixed, despite providing detailed diagnosis and proposed code.

 

From my experience of the past few years, the only "engineering" being done in OpenVMS is generating excuses as to why they don't want to actually fix anything.

A crucible of informative mistakes
Ph Vouters
Valued Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

John,

 

Your reaction is pretty interesting for me. If this is unclear for you, this may be unclear for others. This simply means I did make things clear enough for my readers in my document.

 

Why I do use loop1, loop2 and loop3 ???? loop1 loop saves any previous logical setting for the logicals required by the application. loop2 loop sets all required logicals to their desired target values. The loop3 loop restores all application required logicals to their initial state.

 

Why do I use define instead of define/user ??? Because there can be more than one DCL command executed between loop2 loop and loop3 loop, some commands possibly requiring the exact same logicals settings.

 

As for your remark about a possible typo in the code (LNM$PROCESS instead of PROCESS for the f$trnlnm lexical), it has been checked that specifying "PROCESS" does perfectly the trick on a VMS IA64 V8.3-1H1 system. As a conclusion for this envisionned typo, if specifying "LNM$PROCESS" is the correct way to do, I can state after tests that specifying "PROCESS" is strictly synonymous to LNM$PROCESS in such a DCL context on the computers this DCL code has been tested on.

 

In the hope this clarifies your questions.

Best regards,

Philippe

Ph Vouters
Valued Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

John,

 

If your VMS application summarizes as this:

$ sho logical DECC$EFS_CHARSET
$ spawn/nowait sho logical DECC$EFS_CHARSET
then if the proposed DCL code in my corresponding document contains a define/user instead of a define, this is the result I observe:

$ @template.com
   "DECC$EFS_CHARSET" = "ENABLED" (LNM$PROCESS_TABLE)
%DCL-S-SPAWNED, process PHV_7722 spawned
%SYSTEM-F-NOLOGNAM, no logical name match
$

%SHOW-S-NOTRAN, no translation for logical name DECC$EFS_CHARSET

 

The template.com DCL code here executed is nothing but the code I posted in my Web document.

You can achieve such a spawn DCL emulation using any Java code with calls Runtime.exec(cmd). It ought to underneath call either lib$spawn or one of the HP CRTL exec call.

 

Philippe

H.Becker
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

I'm not sure whether I understand what you are trying to say. For defining user mode logicals, have a look at the HELP file:

 

     User-mode entries are deleted from the process logical name table

     when any image executing in the process exits (that is, after
     a DCL command or user program that executes an image completes
     execution). Also, user-mode logical names are automatically
     deleted when invoking and exiting a command procedure.

$ SHOW LOGICAL is an image so at image exit the logical is deleted. You probably want to try simple examples like:
$ cre define-and-spawn.com
$ define/user hello world
$ spawn show logical hello
 Exit 
$ @ define-and-spawn
%DCL-S-SPAWNED, process BECKER_H_44885 spawned
%DCL-S-ATTACHED, terminal now attached to process BECKER_H_44885
   "HELLO" = "WORLD" (LNM$PROCESS_TABLE)
%DCL-S-RETURNED, control returned to process BECKER_H
$ cre show-and-spawn.com
$ show logical hello 
$ spawn show logical hello
 Exit 
$ define/user hello world
$ @ show-and-spawn.com
   "HELLO" = "WORLD" (LNM$PROCESS_TABLE)
%DCL-S-SPAWNED, process BECKER_H_59866 spawned
%DCL-S-ATTACHED, terminal now attached to process BECKER_H_59866
%SHOW-S-NOTRAN, no translation for logical name HELLO
%DCL-S-RETURNED, control returned to process BECKER_H
$ cre spawn.com
$ spawn show logical hello
 Exit 
$ define/user hello world
$ @ spawn
%DCL-S-SPAWNED, process BECKER_H_13953 spawned
%DCL-S-ATTACHED, terminal now attached to process BECKER_H_13953
   "HELLO" = "WORLD" (LNM$PROCESS_TABLE)
%DCL-S-RETURNED, control returned to process BECKER_H
$ cre define-spawn-and-show.com
$ define/user hello world
$ spawn show logical hello
$ show logical hello
 Exit 
$ @ define-spawn-and-show
%DCL-S-SPAWNED, process BECKER_H_34700 spawned
%DCL-S-ATTACHED, terminal now attached to process BECKER_H_34700
   "HELLO" = "WORLD" (LNM$PROCESS_TABLE)
%DCL-S-RETURNED, control returned to process BECKER_H
   "HELLO" = "WORLD" (LNM$PROCESS_TABLE)
$ cre procedure.com
$ hello:==world
 Exit 
$ cre define-procedure-show.com
$ define/user hello world
$ @procedure
$ show logical hello
 Exit              
$ @ define-procedure-show
%SHOW-S-NOTRAN, no translation for logical name HELLO
John Gillings
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

Phillipe,

   I can see why you have two loops. Logically it makes sense, but DCL is interpretive and therefore slow. Once you've saved a value, you may as well redefine it in the first loop, while you're in the context of that list entry. You get shorter and faster code.

 

>  I can state after tests that specifying "PROCESS" is strictly synonymous to LNM$PROCESS

 

 If that's working on your system, I can only conclude that in your environment there's a logical name "PROCESS" defined in LNM$PROCESS_DIRECTORY which equates to LNM$PROCESS_TABLE. By DCL rules, there won't be any system defined logical names which do not contain a "$" character. If the logical name exists, it must have been defined by something site specific. There's nothing wrong with doing that, but don't expect it to be the same on other systems.  

 

IA64 OpenVMS V8.3-1H1

$ define/process test process_value
$ show log test
   "TEST" = "PROCESS_VALUE" (LNM$PROCESS_TABLE)
$  write sys$output F$TRNLNM("TEST","PROCESS")

$ write sys$output F$TRNLNM("TEST","LNM$PROCESS")
PROCESS_VALUE
$ define/table=lnm$process_directory process lnm$process_table
$ write sys$output F$TRNLNM("TEST","PROCESS")
PROCESS_VALUE

 

 Alpha V8.3

$ define/process test process_value
$ show log test
   "TEST" = "PROCESS_VALUE" (LNM$PROCESS_TABLE)
$ write sys$output F$TRNLNM("TEST","PROCESS")

$ write sys$output F$TRNLNM("TEST","LNM$PROCESS")
PROCESS_VALUE

 

A crucible of informative mistakes
John Gillings
Honored Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

Phillipe,

 

>Because there can be more than one DCL command executed between loop2 loop and loop3 loop

 

   You're correct. If the actions that need to be performed while the logical names are defined involve multiple image activations, then /USER won't help. Regardless, if you're not familiar with the uses of DEFINE/USER, I'd suggest you experiment. It can be very useful, but has some pitfalls you need to know about. As Bob has pointed out SHOW USER runs an image, so is Heisenburgian ;-) You need to be very careful to preserve the state, especially when inserting debug code (WRITE SYS$OUTPUT F$TRNLNM is safe because it doesn't run an image).

 

  Another approach to preserving state I use is with a subprocess. The SPAWNed subprocess inherits the parental environment of logical names and symbols, but don't return any changes. SPAWN can therefore be used to save state. Since PIPE creates a subprocess, and supports multiple statements on a line, a simpler way to code your example is:

 

$ PIPE (DEFINE/NOLOG DECC$EFS_CHARSET           ENABLED ; -
        DEFINE/NOLOG DECC$READDIR_DROPDOTNOTYPE TRUE ; -
        DEFINE/NOLOG DECC$EFS_CASE_PRESERVE     TRUE ; -
        DEFINE/NOLOG JAVA$DELETE_ALL_VERSIONS   TRUE ; -
        YOUR-COMMAND(S) here
       )

 This has the added benefit that you can easily capture the output of the whole command sequence, or pipe it into another command. Yes SPAWN is relatively expensive, but so are multiple loops saving and restoring context. You also need to think about unexpected events between your loop2 and loop3, will they result in failure to restore the context? Using PIPE eliminates that issue because the context of the caller has not been changed.

A crucible of informative mistakes
Ph Vouters
Valued Contributor

Re: If interested with this type of simple solution when dealing with VMS logicals

Dear John,

 

I totally agree with your statements as long as the piped DCL command is short enough. How would you manage for such a much more complex DCL code which incorporates the solution I do propose in my article ? This is a JRuby/VMS DCL script we might propose to the JRuby/VMS end-users. For your knowledge, the time spent to execute the pure DCL code within this script is nuts compared to the time required by the Java code which will interpret the Ruby code even on simple Ruby code samples.

 

Here is the jruby.com Thierry Uso and I were working on:those past days.

.

[philippe@victor ~]$ cat jruby.com

$! +++
$!
$!      JRUBY.COM
$!
$! Author : Thierry USO
$! Date   : 30-JUL-2011
$! Object : run a ruby program
$!
$! Usage  : @jruby [switches] [--] [programfile] [arguments]
$!          @jruby "--help"
$!          @jruby "--1.9" "./hello.rb"
$!
$! Modif  : 10-OCT-2011 better management of logical names
$!
$! ---
$!
$ VERIFY = F$Verify(0)
$!
$ Parse_Style = f$getjpi("", "PARSE_STYLE_PERM")
$ set process/parse=extended
$!
$! ++++
$! get current settings for the logicals you wish to set
$! ----
$!
$ save_env_field1 = "DECC$EFS_CHARSET\ENABLE"
$ save_env_field2 = "DECC$ARGV_PARSE_STYLE\ENABLE"
$ save_env_field3 = "DECC$EFS_CASE_PRESERVE\ENABLE"
$ save_env_field4 = "JAVA$FILENAME_CONTROLS\8"
$ save_env_field5 = "JAVA$CREATE_DIR_WIDTH_OWNER_DELETE\TRUE"
$!
$ i = 1
$ loop1:
$ if f$type(save_env_field'i') .nes. ""
$ then
$   symName = save_env_field'i'
$   logical_name == f$extract(0,f$locate("\",symName),symName)
$   current_logical_value == f$trnlnm("''logical_name'","PROCESS")
$   old_env_field'i' = "''logical_name'\''current_logical_value'"
$   i = i + 1
$   goto loop1
$ endif
$!
$! +++
$! get your jruby directories
$! ---
$!
$ jruby = F$Environment("PROCEDURE")
$ jrubybin = F$Parse(jruby,,,"DEVICE") + F$Parse(jruby,,,"DIRECTORY")
$ jrubyhome = F$Extract(0,F$Locate(".bin]", jrubybin), jrubybin) + "]"
$ call VMS_to_UNIX "''jrubyhome'"
$ jrubyhome = "''UNIX_file_spec'"
$ jrubylib = "''UNIX_file_spec'lib"
$ jffi_home = "''UNIX_file_spec'lib/native/ia64-OpenVMS"
$ delete/symbol/global UNIX_file_spec
$!
$! +++
$! create opt.dat
$! ---
$!
$ open/write opt SYS$SCRATCH:opt.dat
$ write opt "-Djruby.memory.max=-Xmx500m"
$ write opt "-Djruby.stack.max=-Xss1024k"
$ write opt "-Xbootclasspath/a:jruby-1.6.3/lib/jruby-complete.jar"
$ write opt "-Xmx500M"
$ write opt "-Xss1024k"
$ write opt "-Djava.library.path=''jffi_home'"
$ write opt "-Djruby.home=''jrubyhome'"
$ write opt "-Djruby.lib=''jrubylib'"
$ write opt "-Djruby.script=jruby"
$ write opt "-Djruby.shell=/gnu/bin/sh"
$ write opt "-Duser.language=en"
$ write opt "-Duser.country=US"
$ write opt "-Djline.terminal=jline.UnsupportedTerminal"
$ close opt
$!
$! ++++
$! setup the jvm
$! ----
$!
$ if F$Type(JAVA) .eqs. "" then @sys$manager:java$60_setup ! or java$160_setup
$!
$! ++++
$! setup needed DECC and Java logicals
$! ----
$!
$ i = 1
$ loop2:
$ if f$type(save_env_field'i') .nes. ""
$ then
$   symName = save_env_field'i'
$   logical_name == f$extract(0,f$locate("\",symName),symName)
$   logical_value = f$extract(f$locate("\", symName) + 1,-
                    f$length(symName), symName)
$   define/nolog "''logical_name'" "''logical_value'"
$   i = i + 1
$   goto loop2
$ endif
$ !define/nolog decc$argv_parse_style ENABLE
$ !define/nolog decc$efs_case_preserve ENABLE
$ !define/nolog decc$efs_charset ENABLE
$ !define/job/nolog java$filename_controls "8"
$!
$ on error then continue
$!
$ define/user sys$input tt:
$ java -client -cp 'jrubylib'/jruby.jar:'jrubylib'/profile.jar -
"-V" SYS$SCRATCH:opt.dat "org.jruby.Main" -
'P1' 'P2' 'P3' 'P4' 'P5' 'P6' 'P7' 'P8'
$!
$ set process/parse='Parse_style'
$ delete/noconfirm/nolog SYS$SCRATCH:opt.dat;*
$!
$! ++++
$! restore used logicals to their initial value
$! ----
$!
$ i = 1
$ loop3:
$ if f$type(save_env_field'i') .nes. ""
$ then
$   symName = old_env_field'i'
$   logical_name = f$extract(0,f$locate("\",symName),symName)
$   logical_value = f$extract(f$locate("\",symName) +1,-
                    f$length(symName) - f$locate("\", symName) -1,symName)
$   if "''logical_value'" .eqs. ""
$   then
$     deassign "''logical_name'"
$   else
$     define/nolog "''logical_name'" "''logical_value'"
$   endif
$   i = i + 1
$   goto loop3
$ endif
$!
$ if F$Type(VERIFY) .nes. "" then VERIFY = F$Verify(VERIFY)
$!
$ exit
$!
$ VMS_to_UNIX: subroutine
$!
$!
$! We need the directory as a unix-style spec.
$!
$! Don't use f$parse, this way we can maintain ODS-5 syntax, including case
$! We need to let Java$filename_controls do the correct thing.
$!
$! unix_filename = "/" + f$parse(vms_filename,,,"device") - ":"
$!
$! already unix don't convert
$!
$ vms_filename = "''P1'"
$ if f$locate("/",vms_filename) .ne. f$length(vms_filename)
$ then
$       unix_filename = vms_filename
$       UNIX_file_spec :== "''unix_filename'"
$       return
$ endif
$! get the device name
$ if f$locate(":",vms_filename) .ne. f$length(vms_filename)
$ then
$       unix_filename = "/" + -
                F$EXTRACT(0,F$LOCATE(":",vms_filename),vms_filename)
$else
$! relative path
$       unix_filename = "."
$ endif
$!
$!get the directory
$!
$ dir_start_delim = "["
$ dir_end_delim  = "]"
$ if f$locate("<",vms_filename) .ne. f$length(vms_filename)
$ then
$ ! not using [foobar.foo]
$       vms_start_delim = "<"
$       vms_end_delim = ">"
$ endif
$ vms_self_dir = f$extract(f$locate("''dir_start_delim'",vms_filename)+1, -
                        f$locate("''dir_end_delim'",vms_filename) -
                        - f$locate("''dir_start_delim'",vms_filename), -
                        vms_filename) - "[" - "]" - "<" - ">"
$!show sym vms_self_dir
$!
$! Did we have a directory?  No, got to the end
$ if "''vms_self_dir'" .eqs. "" then goto directory_done
$!
$! vms_self_dir = f$parse(vms_filename,,,"directory") - "[" - "]" - "<" - ">"
$ i=0
$uloop:
$ e=f$element(i,".",vms_self_dir)
$ if e .nes. "."
$ then
$   unix_filename = unix_filename + "/" + e
$   i=i+1
$   goto uloop
$ endif
$! put multi-dot back in (Oracle addition)
$! drobles begin
$ real_du = ""
$MULTIDOT_LOOP:
$ if f$length (unix_filename) .EQ. f$locate("^/", unix_filename) -
then goto END_MULTIDOT_LOOP
$ s =  f$extract (0, f$locate ("^/", unix_filename), unix_filename)
$ real_du = real_du + s + "."
$ unix_filename = unix_filename - s - "^/"
$ goto MULTIDOT_LOOP
$ END_MULTIDOT_LOOP:
$ unix_filename = real_du + unix_filename
$! drobles end
$!
$! add trailing "/" for directory
$!
$ directory_done:
$ unix_filename = unix_filename + "/"
$!
$! finally let's get the filename
$!
$ get_filename:
$! unix_filename = f$edit(unix_filename,"lowercase")
$! unix_filename = unix_filename + f$parse(vms_filename,,,"NAME")
$ vms_filename_start = f$locate("''dir_end_delim'",vms_filename)
$!
$! if there wasn't a directory look for a device name
$ if vms_filename_start .eq. f$length(vms_filename)  then -
        vms_filename_start = f$locate(":",vms_filename)
$!
$! if we didn't find a directory or a device, just start from zero
$ if vms_filename_start .eq. f$length(vms_filename)
$ then
        vms_filename_start = 0
$ else
$       vms_filename_start = vms_filename_start + 1 ! move beyond the delim
$ endif
$!
$ unix_filename = unix_filename + -
        f$extract(vms_filename_start, -
                  f$length(vms_filename), vms_filename)
$ real_du = ""
$ MULTIDOT_LOOP_FILE:
$ if f$length (unix_filename) .EQ. f$locate("^.", unix_filename) then -
        goto END_MULTIDOT_LOOP_FILE
$ s =  f$extract (0, f$locate ("^.", unix_filename), unix_filename)
$ real_du = real_du + s + "."
$ unix_filename = unix_filename - s - "^."
$ goto MULTIDOT_LOOP_FILE
$ END_MULTIDOT_LOOP_FILE:
$ unix_filename = real_du + unix_filename
$ UNIX_file_spec :== "''unix_filename'"
$ return
$ endsubroutine