Author Topic: List path to ObjectID's in plain rexx  (Read 382 times)

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
List path to ObjectID's in plain rexx
« on: September 11, 2019, 10:50:05 pm »
This script take the object ID that point to a program object such as '<WP_CLIPV>' or a folder such as '<MMPM2_FOLDER>' and return path information.
  • It can list the file path to the object, path to the application (for WPProgram) and the path to the object with names as seen visually on the desktop.
  • The code use rexx and rexxutils without the need for wptools.
  • Haven't tried, but it's possible to add information about startup folder and parameters for WPProgram objects.
  • The script cache information during the first call so that consecutive calls return faster.
  • Use fastini to shorten the initial startup delay more.

You may want to optimize/shorten the script  ;)

Code: [Select]
/*
 * Filename: WPSObj.cmd
 *   Author: JanErik
 *  Created: Sat Aug  3 2019
 *  Purpose: Script to list Paths and Object names of Object ID's with rexx
 *  Changes:
 */
CALL rxFuncAdd "SysIni", "REXXUTIL", "SysIni"

objids = 'WP_NOWHERE NOOBJECT WP_CHESS WP_GAMES WP_TOOLS WP_SEEK XWP_FONTFOLDER WP_3DCAD MAH_FOLDER MAH_EXE'
DO i = 1 TO WORDS( objids )
   hID = RxObjIDPath( '<'||SUBWORD( objids, i, 1 )||'>' )
   SAY hID '"'!_PWH_!.hID.appPath'" ->' !_PWH_!.hID.fPath '"'!_PWH_!.hID.fName'"' '<-'||!_PWH_!.hID.isDir||'->' !_PWH_!.hID.oPath '"'!_PWH_!.hID.oName'"'
END
RETURN 0

RxObjIDPath: PROCEDURE EXPOSE !_PWH_!.
   PARSE ARG objID, refresh
   IF \DATATYPE( !_PWH_!.0, 'W' ) | refresh = 1 THEN
   DO
      /* Determine the currently active handles */
      hApp = SysIni( 'SYSTEM', 'PM_Workplace:ActiveHandles', 'HandlesAppName' )
      IF hApp = 'ERROR:' THEN
         hApp = 'PM_Workplace:Handles0'
      block = ''
      CALL SysIni 'SYSTEM', hApp, 'All:', 'blocks.'
      DO i = 1 To blocks.0
         block = block||SysIni( 'SYSTEM', hApp, blocks.i )
      END
      DROP blocks.
      DROP hApp
      !_PWH_!. = ''
      !_count_! = 0
      PARSE VALUE block WITH 5 block
      DO WHILE 0 < LENGTH( block )
         PARSE VALUE block WITH !_name_! 5 block
         SELECT
            WHEN !_name_! = 'DRIV' THEN
               PARSE VALUE block WITH 20 block
            WHEN !_name_! = 'NODE' THEN
            DO
               PARSE VALUE block WITH 3 !_oID_! 5 !_pID_! 7 25 !_isDir_! 27 !_fnSize_! 29 block
               !_oID_! = C2RX( !_oID_! )
               !_PWH_!.!_oID_!.isDir = C2X2D( !_isDir_! )
               !_PWH_!.!_oID_!.fID = C2RX( !_pID_! )
               !_PWH_!.!_oID_!.fPath = ''
               !_fnSize_! = C2X2D( !_fnSize_! )
               !_PWH_!.!_oID_!.fName = LEFT( block, !_fnSize_! )
               !_count_! = !_count_! + 1
               !_PWH_!.0 = !_count_!
               block = SUBSTR( block, !_fnSize_! + 2 )
            END
            OTHERWISE NOP
         END
      END
      IF SysIni( 'USER', 'PM_Abstract:FldrContent', 'All:', 'blocks.' ) <> 'ERROR:' THEN
      DO i = 1 TO blocks.0
         !_children_! = SysIni( 'USER', 'PM_Abstract:FldrContent', blocks.i )
         block = blocks.i
         IF !_PWH_!.block.isDir <> 1 THEN
            !_PWH_!.block.isDir = 1
         DO WHILE 0 < LENGTH( !_children_! )
            PARSE VALUE !_children_! WITH !_child_! 3 5 !_children_!
            !_child_! = C2RX( !_child_! )
            IF !_PWH_!.!_child_!.fID = '' THEN
               !_PWH_!.!_child_!.fID = block
         END
      END
      DROP blocks.
      DROP block
      DROP !_child_!
      DROP !_children_!
   END
   IF 0 < LENGTH( STRIP( objID ) ) THEN
   DO
      !_findID_! = C2RX( LEFT( SysIni( 'USER', 'PM_Workplace:Location', objID ), 2 ) )
      !_PWH_!.!_findID_!.oPath = RxObjPath( !_findID_! )
      RETURN !_findID_!
   END
RETURN 0
   
RxObjPath: PROCEDURE EXPOSE !_PWH_!.
   PARSE ARG findID
   !_path_! = ''
   !_PWH_!.findID.appPath = ''
   PARSE VALUE SysIni( 'USER', 'PM_Abstract:Objects', STRIP( findID, 'L', '0' ) ) WITH . 'WPProgramRef' +19 !_findID_! +2 'WPAbstract' +15 !_objSize_! +2 !_objName_! 'WPObject'
   IF !_findID_! <> '' THEN
   DO
      !_findID_! = C2RX( !_findID_! )
      IF 0 < LENGTH( !_objSize_! ) THEN
         !_PWH_!.findID.oName = LEFT( !_objName_!, C2X2D( !_objSize_! ) - 1 )
      IF !_PWH_!.!_findID_!.isDir <> 1 THEN
      DO
         !_PWH_!.findID.fName = !_PWH_!.findID.oName
         DO WHILE 0 < !_PWH_!.!_findID_!.fID
            !_path_! = '\'||!_PWH_!.!_findID_!.fName||!_path_!
            !_findID_! = !_PWH_!.!_findID_!.fID
         END
         !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
         !_PWH_!.findID.appPath = !_path_!
      END
      !_findID_! = !_PWH_!.findID.fID
   END
   ELSE IF !_PWH_!.!_findID_!.isDir <> 1 THEN
      !_findID_! = !_PWH_!.findID.fID
   ELSE
      !_findID_! = findID
   !_path_! = ''
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      !_path_! = '\'||!_PWH_!.!_findID_!.fName||!_path_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
   !_PWH_!.findID.fPath = !_path_!
   !_path_! = !_path_!||'\'||!_PWH_!.findID.fName
   !_findID_! = findID
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      PARSE VALUE REVERSE( !_path_! ) WITH !_name_!'\'!_path_!
      !_name_! = REVERSE( !_name_! )
      !_path_! = REVERSE( !_path_! )
      IF SysGetEA( !_path_!||'\'||!_name_!, '.LONGNAME', 'EAValue' ) = 0 & EAValue <> '' THEN
      DO
         PARSE VAR EAValue EAType +2 EALength +2 EAValue
         IF EAType = 'FDFF'x THEN
            !_PWH_!.!_findID_!.oName = STRIP( EAValue, 'T', '00'x )
      END
      ELSE
         !_PWH_!.!_findID_!.oName = !_name_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = ''
   !_findID_! = !_PWH_!.findID.fID
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      !_path_! = '\'||!_PWH_!.!_findID_!.oName||!_path_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
RETURN !_path_!

XREV: PROCEDURE
   !_hex_! = STRIP( ARG( 1 ) )
   !_exp_! = ''
   IF \DATATYPE( !_hex_!, 'X' ) THEN RETURN !_exp_!
   DO i = 1 TO LENGTH( !_hex_! ) BY 4
      !_exp_! = STRIP( TRANSLATE( '1234', RIGHT( '0000'||STRIP( SUBSTR( !_hex_!, i, 4 ) ), 4 ), '3412' ) )||!_exp_!
   END
RETURN !_exp_!

C2RX: PROCEDURE
RETURN XREV( C2X( ARG(1) ) )

C2X2D: PROCEDURE
   chrs = ARG(1)
   retval = ''
   DO i = 1 TO LENGTH( chrs ) BY 2
      retval = retval||X2D( C2RX( SUBSTR( chrs, i, 2 ) ) )
   END
RETURN retval

//Jan-Erik

André Heldoorn

  • Hero Member
  • *****
  • Thank You
  • -Given: 113
  • -Receive: 19
  • Posts: 770
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #1 on: September 12, 2019, 09:35:46 am »
An impressive but slow part of WPTOOOLS.DLL in Rexx. A possible bug, depending on an unknown goal, is appending the assumed, recommended and usual <> characters, which aren't required.

  • The script cache information during the first call so that consecutive calls return faster.

It may be possible to reduce the total number of SysIni()-calls with 'All:' to one, instead of once per sample object.

Besides that, after having inserted a CALL Time 'R' and SAY Time('E'):

First call of the whole script, all sample objects, Object Rexx interpreter: 86.74 seconds.
Second call of the whole script, only <WP_GAMES>: 95.32 seconds.
Third call of the whole script, only <WP_GAMES>: 102.43 seconds.
Fourth call of the whole script, only <WP_GAMES>: 313.86 seconds (executed in the background).

IIRC a possible improvement w.r.t. WPTOOLS.DLL, without introducing new functionalities, is that is does display some default settings as if those settings were customized. IIRC/2 mainly the German OS/2 community tends to copy code to set the program type of, for example, a PM program with program type PM to PM, probably because WPTOOLS.DLL returned that (default) setting. If someone is going to add (displaying) the EXENAME, PARAMETERS, and so on, then please consider an option to unhide such a modern default setting. Typically "PROGTYPE=PM" is not required in a WPS setup string of a PM app, albeit WPTOOLS.DLL may display this as if it's set intentionally. A reason to display it may be that WPTOOLS.EXE doesn't check the type of the app itself, but typically that type will be right for a fully functional PM app.

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #2 on: September 12, 2019, 12:40:03 pm »
Retrieval of the values for object id's in the users ini-file require < and >
Measure the time to retrieve the first object compared to the second, third etc. not the script itself.
It has to load and build entries from the ini-files the first time.
Object Rexx use built in functionality, so this is intended for Classic Rexx.
External libraries are seldom a part of the base OS and call for something as this.

Code: [Select]
IF \DATATYPE( !_PWH_!.0, 'W' ) | refresh = 1 THEN restrict the number of times the script has to fill the stem to only the first time while it retrieve the entry once for each <objectid> with
Code: [Select]
PARSE VALUE SysIni( 'USER', 'PM_Abstract:Objects', STRIP( findID, 'L', '0' ) ) WITH . 'WPProgramRef' +19 !_findID_! +2 'WPAbstract' +15 !_objSize_! +2 !_objName_! 'WPObject'

André Heldoorn

  • Hero Member
  • *****
  • Thank You
  • -Given: 113
  • -Receive: 19
  • Posts: 770
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #3 on: September 12, 2019, 02:57:32 pm »
Retrieval of the values for object id's in the users ini-file require < and >."
 
Measure the time to retrieve the first object compared to the second, third etc. not the script itself.

External libraries are seldom a part of the base OS and call for something as this.

No, the characters are a part of the object ID. First you have left out those characters, and next you always append then. Like changing your first name to an-Erik, next always use a J prefix, and hope Erik-Jan will never use the app (an-Erik -> Jan-Erik, Erik-Jan -> JErik-Jan). The name of the object ID of the desktop is "<WP_DESKTOP>", including the <> characters. The OS, or apps, don't have to append it. You actually may end up with <<WP_DESKTOP>>, if the users types a correct name and you won't check the name. HELP REXX SYSCREATEOBJECT is an example, which shows that the object ID isn't "WP_DESKTOP". It's "<WP_DESKTOP>". IIRC I've seen at least one app which didn't use the <> characters.

I've measured what I wanted to measure, which was the script itself. It slows down too. Different measurement, different subject.

So is your script. But AFAICT, I don't have ArcaOS, is there an OS file called WPTOOLS.DLL in the DLL directory of that OS, and it would be easy to release WPTools as an informal "User's FixPak" for OS/2s and eCSes, if you can find the same full set of WPTools files. Unfortunately people have added "stuff" to OSes without thinking about the rest of the small community (7-zip, WPTools, OO.EXE, ...), so you may have missed the fact that this external module actually is a part of the base OS by now. Nevertheless the dive into the INI files remains impressive, and may have some use on its own.

Dave Yeo

  • Hero Member
  • *****
  • Thank You
  • -Given: 11
  • -Receive: 149
  • Posts: 2114
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #4 on: September 12, 2019, 04:32:20 pm »
But AFAICT, I don't have ArcaOS, is there an OS file called WPTOOLS.DLL in the DLL directory of that OS, and it would be easy to release WPTools as an informal "User's FixPak" for OS/2s and eCSes, if you can find the same full set of WPTools files. Unfortunately people have added "stuff" to OSes without thinking about the rest of the small community (7-zip, WPTools, OO.EXE, ...), so you may have missed the fact that this external module actually is a part of the base OS by now. Nevertheless the dive into the INI files remains impressive, and may have some use on its own.

WPTools is in the DLL directory on ArcaOS (sys\dll) and eCS 2.1+ (ecs\dll).
While oo.exe is in both ArcaOS and eCS2.1+, 7z is not installed as part of ArcaOS, just eCS 2.1+

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #5 on: September 12, 2019, 05:39:22 pm »
Very good  :)

One can modify the first part to measure the time.
Once cached (as seen on the first line), each call to retrieve objects doesn't take long.
Code: [Select]
objids = 'WP_NOWHERE NOOBJECT WP_CHESS WP_GAMES WP_TOOLS WP_SEEK XWP_FONTFOLDER WP_3DCAD MAH_FOLDER MAH_EXE'
DO i = 1 TO WORDS( objids )
   CALL TIME 'R'
   hID = RxObjIDPath( '<'||SUBWORD( objids, i, 1 )||'>' )
   SAY hID '"'!_PWH_!.hID.appPath'" ->' !_PWH_!.hID.fPath '"'!_PWH_!.hID.fName'"' '<-'||!_PWH_!.hID.isDir||'->' !_PWH_!.hID.oPath '"'!_PWH_!.hID.oName'" (' TIME( 'E' ) ')'
END
RETURN 0

output:
Code: [Select]
E0E8 "" -> C: "NOWHERE" <-1-> C: "Nowhere" ( 2.130000 )
5245 "" ->  "" <-->  "" ( 0 )
DC91 "C:\OS2\APPS\OS2CHESS.EXE" -> C:\DESKTOP\PROGRAMS\GAMES "Chess" <--> C:\Desktop\Programs\Games "Chess" ( 0.040000 )
84AE "" -> C:\DESKTOP\PROGRAMS "GAMES" <-1-> C:\Desktop\Programs "Games" ( 0 )
6D42 "" -> C:\DESKTOP\PROGRAMS "UTILITIES" <-1-> C:\Desktop\Programs "Utilities" ( 0.010000 )
9DE6 "C:\OS2\APPS\PMSEEK.EXE" -> C:\DESKTOP\PROGRAMS\UTILITIES "Seek and Scan Files" <--> C:\Desktop\Programs\Utilities "Seek and Scan Files" ( 0 )
68A2 "" -> C:\DESKTOP\COMPUTER\SYSTEM SETUP "FONTS" <-1-> C:\Desktop\Computer\System Setup "Fonts" ( 0.030000 )
097C "D:\PROGRAMS\VIRTUALBOX\BIN\3DCAD.CMD" -> C:\DESKTOP\COMPUTER\COMMAND PROMPTS "3D CAD" <--> C:\Desktop\Computer\Command Prompts "3D CAD" ( 0.010000 )
C150 "" -> C:\DESKTOP\PROGRAMS\GAMES "MAHJONGG SOLITAIRE" <-1-> C:\Desktop\Programs\Games "Mahjongg Solitaire" ( 0.010000 )
2D7C "C:\OS2\APPS\MAHJONGG.EXE" -> C:\DESKTOP\PROGRAMS\GAMES\MAHJONGG SOLITAIRE "Mahjongg" <--> C:\Desktop\Programs\Games\Mahjongg Solitaire "Mahjongg" ( 0 )

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: List path to ObjectID's in plain rexx
« Reply #6 on: September 16, 2019, 06:30:40 pm »
This version can also list files as shadows and not only <WP_OBJECTID>.
It list only one of duplicate named shadows.
< and > has to be specified for objects to be able to retrieve the entry from SysIni.

Code: [Select]
/*
 * Filename: WPSObj.cmd
 *   Author: JanErik
 *  Created: Sat Aug  3 2019
 *  Purpose: Script to list Paths and Object names of Object ID's with rexx
 *  Changes:
 */
call rxFuncAdd "SysIni", "REXXUTIL", "SysIni"
IF ARG(1) = 1 THEN
   RETURN RxShadows()

objids = 'Bitmap  Movies TAJMAHAL.MAH REXXLIB.ZIP FireFox'
DO i = 1 TO WORDS( objids )
   CALL TIME 'R'
   hID = RxObjIDPath( SUBWORD( objids, i, 1 ) )
   SAY hID '"'!_PWH_!.hID.appPath'" ->' !_PWH_!.hID.fPath '"'!_PWH_!.hID.fName'"' '<-'||!_PWH_!.hID.isDir||'->' !_PWH_!.hID.oPath '"'!_PWH_!.hID.oName'" (' TIME( 'E' )'s )'
END
RETURN 0

RxObjIDPath: PROCEDURE EXPOSE !_PWH_!.
   PARSE ARG objID, refresh
   IF \DATATYPE( !_PWH_!.0, 'W' ) | refresh = 1 THEN
   DO
      /* Process shortcuts to shadows in parallell */
      '@START "WPShadow" /C /MIN WPSObj 1'
      /* Determine the currently active handles */
      hApp = SysIni( 'SYSTEM', 'PM_Workplace:ActiveHandles', 'HandlesAppName' )
      IF hApp = 'ERROR:' THEN
         hApp = 'PM_Workplace:Handles0'
      block = ''
      CALL SysIni 'SYSTEM', hApp, 'All:', 'blocks.'
      DO i = 1 To blocks.0
         block = block||SysIni( 'SYSTEM', hApp, blocks.i )
      END
      DROP blocks.
      DROP hApp
      !_PWH_!. = ''
      !_count_! = 0
      PARSE VALUE block WITH 5 block
      DO WHILE 0 < LENGTH( block )
         PARSE VALUE block WITH !_name_! 5 block
         SELECT
            WHEN !_name_! = 'DRIV' THEN
               PARSE VALUE block WITH 20 block
            WHEN !_name_! = 'NODE' THEN
            DO
               PARSE VALUE block WITH !_pre_! 3 !_oID_! 5 !_pID_! 7 !_mid_! 25 !_isDir_! 27 !_fnSize_! 29 block
               !_oID_! = C2RX( !_oID_! )
               !_PWH_!.!_oID_!.isDir = C2X2D( !_isDir_! )
               !_PWH_!.!_oID_!.fID = C2RX( !_pID_! )
               !_PWH_!.!_oID_!.fPath = ''
               !_fnSize_! = C2X2D( !_fnSize_! )
               !_PWH_!.!_oID_!.fName = LEFT( block, !_fnSize_! )
               !_count_! = !_count_! + 1
               !_PWH_!.0 = !_count_!
               block = SUBSTR( block, !_fnSize_! + 2 )
            END
            OTHERWISE NOP
         END
      END
      IF SysIni( 'USER', 'PM_Abstract:FldrContent', 'All:', 'blocks.' ) <> 'ERROR:' THEN
      DO i = 1 TO blocks.0
         !_children_! = SysIni( 'USER', 'PM_Abstract:FldrContent', blocks.i )
         block = blocks.i
         IF !_PWH_!.block.isDir <> 1 THEN
            !_PWH_!.block.isDir = 1
         DO WHILE 0 < LENGTH( !_children_! )
            PARSE VALUE !_children_! WITH !_child_! 3 5 !_children_!
            !_child_! = C2RX( !_child_! )
            IF !_PWH_!.!_child_!.fID = '' THEN
               !_PWH_!.!_child_!.fID = block
         END
      END
      DROP blocks.
      DROP block
      DROP !_child_!
      DROP !_children_!
   END
   IF 0 < LENGTH( STRIP( objID ) ) THEN
   DO
      !_findID_! = SysIni( 'USER', 'PM_Workplace:Location', objID )
      IF !_findID_! = 'ERROR:' THEN
         !_findID_! = SysIni( 'SHADOW.INI', 'PM_Shadow:Location', SysMapCase( objID ) )
      IF !_findID_! <> 'ERROR:' THEN
      DO
         PARSE VALUE !_findID_! WITH !_findID_! +2 +2 !_objName_!
         !_findID_! = C2RX( !_findID_! )
         !_PWH_!.!_findID_!.oPath = RxObjPath( !_findID_! )
         RETURN !_findID_!
      END
   END
RETURN 0
   
RxShadows: PROCEDURE
   IF SysIni( 'USER', 'PM_Abstract:Objects', 'All:', 'shadow.' ) <> 'ERROR:' THEN
   DO i = 1 TO shadow.0
      PARSE VALUE SysIni( 'USER', 'PM_Abstract:Objects', shadow.i ) WITH . 'WPShadow' . 'WPAbstract' +15 !_objSize_! +2 !_objName_! 'WPObject'
      IF 0 < LENGTH( !_objName_! ) THEN
         CALL SysIni 'SHADOW.INI', 'PM_Shadow:Location', SysMapCase( LEFT( !_objName_!, C2X2D( !_objSize_! ) - 1 ) ), X2C( XREV( shadow.i ) )
   END
RETURN 0
   
RxObjPath: PROCEDURE EXPOSE !_PWH_!.
   PARSE ARG findID
   !_path_! = ''
   !_PWH_!.findID.appPath = ''
   PARSE VALUE SysIni( 'USER', 'PM_Abstract:Objects', STRIP( findID, 'L', '0' ) ) WITH !_pre_! 'WPAbstract' +15 !_objSize_! +2 !_objName_! 'WPObject'
   PARSE VALUE !_pre_! WITH . 'WPProgramRef' +19 !_findID_! +2
   IF !_findID_! = '' THEN
      PARSE VALUE !_pre_! WITH . 'WPShadow' +15 !_findID_! +2
   IF !_findID_! <> '' THEN
   DO
      !_findID_! = C2RX( !_findID_! )
      IF 0 < LENGTH( !_objSize_! ) THEN
         !_PWH_!.findID.oName = LEFT( !_objName_!, C2X2D( !_objSize_! ) - 1 )
      IF !_PWH_!.!_findID_!.isDir <> 1 THEN
      DO
         !_PWH_!.findID.fName = !_PWH_!.findID.oName
         DO WHILE 0 < !_PWH_!.!_findID_!.fID
            !_path_! = '\'||!_PWH_!.!_findID_!.fName||!_path_!
            !_findID_! = !_PWH_!.!_findID_!.fID
         END
         !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
         !_PWH_!.findID.appPath = !_path_!
      END
      !_findID_! = !_PWH_!.findID.fID
   END
   ELSE IF !_PWH_!.!_findID_!.isDir <> 1 THEN
      !_findID_! = !_PWH_!.findID.fID
   ELSE
      !_findID_! = findID
   !_path_! = ''
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      !_path_! = '\'||!_PWH_!.!_findID_!.fName||!_path_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
   !_PWH_!.findID.fPath = !_path_!
   !_path_! = !_path_!||'\'||!_PWH_!.findID.fName
   !_findID_! = findID
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      PARSE VALUE REVERSE( !_path_! ) WITH !_name_!'\'!_path_!
      !_name_! = REVERSE( !_name_! )
      !_path_! = REVERSE( !_path_! )
      IF SysGetEA( !_path_!||'\'||!_name_!, '.LONGNAME', 'EAValue' ) = 0 & EAValue <> '' THEN
      DO
         PARSE VAR EAValue EAType +2 EALength +2 EAValue
         IF EAType = 'FDFF'x THEN
            !_PWH_!.!_findID_!.oName = STRIP( EAValue, 'T', '00'x )
      END
      ELSE
         !_PWH_!.!_findID_!.oName = !_name_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = ''
   !_findID_! = !_PWH_!.findID.fID
   DO WHILE 0 < !_PWH_!.!_findID_!.fID
      !_path_! = '\'||!_PWH_!.!_findID_!.oName||!_path_!
      !_findID_! = !_PWH_!.!_findID_!.fID
   END
   !_path_! = !_PWH_!.!_findID_!.fName||!_path_!
RETURN !_path_!

XREV: PROCEDURE
   !_hex_! = STRIP( ARG( 1 ) )
   !_exp_! = ''
   IF \DATATYPE( !_hex_!, 'X' ) THEN RETURN !_exp_!
   DO i = 1 TO LENGTH( !_hex_! ) BY 4
      !_exp_! = STRIP( TRANSLATE( '1234', RIGHT( '0000'||STRIP( SUBSTR( !_hex_!, i, 4 ) ), 4 ), '3412' ) )||!_exp_!
   END
RETURN !_exp_!
   
C2R2X: PROCEDURE
   chrs = ARG(1)
   retval = ''
   DO i = 1 TO LENGTH( chrs ) BY 2
      retval = retval||'|'||C2RX( SUBSTR( chrs, i, 2 ) )
   END
RETURN retval   
   
C2RX: PROCEDURE
RETURN XREV( C2X( ARG(1) ) )

C2X2D: PROCEDURE
   chrs = ARG(1)
   retval = ''
   DO i = 1 TO LENGTH( chrs ) BY 2
      retval = retval||X2D( C2RX( SUBSTR( chrs, i, 2 ) ) )
   END
RETURN retval