AOH :: P19-02.TXT

DCL Utilities for VMS Hackers



                               ==Phrack Inc.==

                     Volume Two, Issue 19, Phile #2 of 8

                       DCL Utilities for the VMS Hacker
                       >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                                      By
                                  The Mentor

                       Special thanks to Silver Spy for
                   turning me onto DCL in the first place!
------------------------------------------------------------------------------

     Anyone who spends time hacking on VAXes (by hacking, I don't just mean
trying to get in... I mean *doing* something once you're in!) notices that the
DCL command language is extremely powerful.  I have put together a selection
of utilities that not only should prove helpful to the hacker, but serve as a
good example of programming in DCL.
     Every attempt has been made to preserve unchanged the user-environment
from the initialization of the file to the exit.  Any session-permanent
changes are documented.

                            Brief Overview of DCL
                            >>>>>>>>>>>>>>>>>>>>>

     There are numerous files out there on DCL (the VMS help files are the
best place to find information), so I'm not going to teach you how to program
in it.  To use the following code, isolate the section of code you want in
your favorite text editor, upload it into a file, and name the file
<progname>.COM.  Anytime you see a file ending with .COM, you know it's a DCL
file.  DCL files are executed by issuing the command
                       $@FILENAME
or, in the case of a file you want to run as a separate process,
                       $SPAWN/NOWAIT @FILENAME

                              Table of Contents
                              >>>>>>>>>>>>>>>>>

     1. CD.DOC     :  This is the documentation for CD.COM (and the only
                      documentation file in the bunch.
     2. CD.COM     :  A change directory utility, much like the PC command
                      CD, except more powerful.  $SET DEFAULT is a pain in
                      the ass!
     3. HUNT.COM   :  Searches a specified node for a given user.  Useful
                      for alerting you to the presence of a sysop.
     4. ALARM.COM  :  An alarm clock.  If they check the logs at 8 a.m., you
                      probably want to be off before then.
     5. CGO.COM    :  Included because it's short.  Allows you to compile,
                      link, and run a C program with a one-line command.


     I have about 300 more pages of COM files.  If you need anything, drop me
a line.  I'll try and help out.  I can be found on Forgotten Realm, or you can
call a non-hacker (local to me) IBM game board if it's an urgent message (The
Bastille-- 512/353-0590  300/1200  24 hrs.  It's not the best hacker board in
the world, but my mail arrives daily...)

     Also, if programming of this type interests you, let me know!  I'm
considering putting up a board for the discussion of programming (compilers,
AI/Expert Systems, Op Systems, etc...).  If I get enough positive response,
I'll go with it.  Leave mail on the aforementioned systems.

                                                The Mentor




       CD.COM   Version 5.0   VMS Change Directory Command


       Sub-directories are a nice feature on many computers, but
       they're not always easy to take advantage of.  The VMS
       commands to access sub-directories are a little obscure,
       even to PC programmers who are used to using directories.

       The solution?  CD.COM, a change directory command that works
       almost the same as the PC-DOS CD and PROMPT commands:

          CD              - Display your home directory, current
                            directory, and node name.  (Similar to, but
                            better than the VMS SHOW DEFAULT command.)

          CD dir_name     - Move you to the [dir_name] directory.
          CD [dir_name]     (Same as the SET DEFAULT [dir_name] command.)

          CD .sub_name    - Move you to the [.sub_name] subdirectory.
          CD [.sub_name]    (Same as the SET DEFAULT [.sub_name] command.)

          CD \            - Move you to your home (root) directory, which
          CD HOME           is the directory you are in when you login.
          CD SYS$LOGIN      (Same as the SET DEFAULT SYS$LOGIN command.)

          CD ..           - Move you to the directory above your
          CD [-]            current directory. (Same as the VMS
                            SET DEFAULT [-] command.)

          CD ..sub_name   - Move you "sideways" from one subdirectory
          CD [-.sub_name]   to another subdirectory. (Same as the
                            SET DEFAULT [-.sub_name] command.)

          CD *            - Select a subdirectory to move to, from a
                            list of subdirectories.

          CD .            - Reset the current directory.

          CD ?            - Display instructions for using CD.

       The VMS SET DEFAULT command has a flaw: you can change
       directories to a directory that doesn't exist.  CD handles this
       more elegantly; you're left in the same directory you were in
       before, and this message appears:

            [dir_name] Directory does not exist!

       PC-DOS lets you display the current directory as part of the
       prompt.  (If you haven't seen this feature, try the PC-DOS
       command PROMPT $P$G.)  CD.COM will change the prompt for you
       each time you change directories if you include this line in
       your LOGIN.COM file:

          DEFINE SYS$PROMPT "ON"

       Without this line, your prompt is not changed from what you
       have it set as.  Instead, your home (root) directory name,
       current directory name, and node name are displayed whenever
       you issue the CD command.

       Since VMS allows prompts to contain no more than 32 characters,
       if you change to a subdirectory that would make your prompt too
       long, CD automatically leaves off some of the higher level
       sub-directories to keep your prompt short, and displays a "*"
       as one of the prompt characters.

       CD lets you use directory names defined with with the DEFINE
       command.  For example, if you're in one of Dr. Smiths' CS3358
       classes, you might want to define his CS3358 assignments
       directory like this:

          DEFINE SMITH "DISK$CS:[CS.SMITH.3358]"

       Then, CD SMITH would move you to this directory.  Try it!
       Also, some directories are already defined by the system.
       The SHOW LOGICAL command will give you clues to some of these
       system directories, if you want to go exploring.  CD also
       supports the use of symbols for directory names.

       Like with PC-DOS, VMS directories and sub-directories are tree
       structured.  The system root directory for your disk has the
       name [000000], and in it are the names of all the sub-directories
       for your disk.  The directories for an imaginary user, CS335825305,
       would be located like this:

  System Root Directory:
                                  [000000]
                               .   .   .   .
  CS3358 Directories:    .        .     .        .
                   .             .      *.             .
       ... [CS3358251]   [CS3358252]   [CS3358253]   [CS3358254] ...
                                      .   .      .
  CS3358253 Directories:        .         .           .
                          .              *.               .
       ... [CS3358253.04HOPE]   [CS3358253.05JONES]   [CS3358253.06KEY] ...
                                       .    .
  CS335825305 Directories:            .      .
                                    *.       *.
                 [CS3358253.05JONES.MAIL]  [CS3358253.05JONES.BULL]


       If you're not using sub-directories, but want to, you can
       create them with the CREATE command:

           CREATE/DIR  [.sub_name]

       VMS allows directories to be seven or eight levels deep, but
       one or two levels is enough for most users.

       VMS also allows the symbols < and > to be used instead of
       [ and ], to specify directory names. CD fully supports this.

                               Code for CD.COM
                               >>>>>>>>>>>>>>>

$! CD.COM v6.09
$! The Ultimate Change Directory Command.
$!
$  hdir     = f$trnlnm("SYS$LOGIN")                 ! Home Directory
$  ndir     = f$edit(p1,"UPCASE")                   ! New  Directory
$  odir     = f$environment("DEFAULT")              ! Old  Directory
$  prompton = (f$edit(f$trnlnm("SYS$PROMPT"),"UPCASE") .eqs. "ON")
$!
$  if (ndir .eqs. "")           then goto DISPLAY   ! No Dir
$  if (ndir .eqs. "*")          then goto DIRSEARCH ! Search for Dirs
$  if (ndir .eqs. "?")          then goto HELP      ! Instructions
$!
$  PARSE:
$  length   = f$length(ndir)                        ! Fix up ndir
$  if (f$location("@",ndir) .eq. 0) .or. -
      (f$location("$",ndir) .eq. 0) then ndir = f$extract(1, length - 1, ndir)
$  right    = f$location("]",ndir) + 1
$  if (right .gt. length) then right = f$location(">", ndir)
$  if (right .le. length) then ndir  = f$extract(0, right, ndir)
$!
$  if (f$trnlnm(ndir) .eqs. "") then goto CASESYM   ! Not Logical Name
$     ndir   = f$trnlnm(ndir)                       ! Logical Name
$     goto PARSE
$!
$  CASESYM:
$  if ("''&ndir'" .eqs. "")     then goto CASE0     ! Not Symbol
$     ndir = 'ndir'                                 ! Symbol
$     goto PARSE
$!
$  CASE0:
$  len_ndir = f$length(ndir)                        ! Regular Dir
$  if (f$location("[", ndir) .lt. len_ndir) .or. -
      (f$location("<", ndir) .lt. len_ndir) then goto SETDIR
$!
$  CASE1:                                           ! Home Dir
$  if ((ndir .nes. "HOME") .and. (ndir .nes. "\")) then goto CASE2
$     ndir = hdir
$     goto SETDIR
$!
$  CASE2:                                           ! . .. .dir
$  if (f$location(".", ndir) .nes. 0) then goto CASE3
$     if (ndir .eqs. "..") then ndir = "-"
$     if (f$extract(0, 2, ndir) .eqs. "..") -
         then ndir = "-" + f$extract(1, len_ndir - 1, ndir)
$     ndir = "[" + ndir + "]"
$     if (ndir .eqs. "[.]") then ndir = odir
$     goto SETDIR
$!
$  CASE3:                                           ! :
$  if (f$location(":", ndir) .ge. len_ndir) then goto CASE4
$     left    = f$location(":", ndir) + 1
$     symbol  = f$extract(left, 1, ndir)
$     if (symbol .eqs. ":")  then goto CASE3B       ! :: Node
$     if ((symbol .eqs. "[") .or. (symbol .eqs. "<")) then goto SETDIR
$        ndir = f$extract(0, left, ndir) + "[" -
              + f$extract(left, len_ndir - left+1, ndir) + "]"
$     goto SETDIR
$!
$  CASE3B:                                          ! NODE::nothing
$  if (f$length(ndir)-1 .gt. left) then goto CASE3C
$     ndir = ndir + "[000000]"
$     goto SETDIR
$!
$  CASE3C:                                          ! NODE::directory
$  if ((f$location("[", ndir) - f$location("<", ndir)) .ne. 0) -
      then goto SETDIR
$
$     ndir = f$parse(ndir,,,"NODE") + "[" + f$parse(ndir,,,"NAME") + "]"
$     goto SETDIR
$!
$  CASE4:                                           ! dir
$  ndir = "[" + ndir + "]"
$!
$  SETDIR:
$  set default 'ndir'
$  if (f$parse("") .eqs. "") then goto DIRERROR
$!
$  DISPLAY:
$  if ((ndir .nes. "") .and. prompton) then goto NODISPLAY
$     hnode = f$getsyi("NODENAME")
$     cnode = f$parse(f$trnlnm("SYS$DISK"),,,"NODE") - "::"
$     if (cnode .eqs. "") then cnode = hnode
$     cdir  = f$environment("DEFAULT")
$     write sys$output " "
$     write sys$output "          Home Node: ", hnode
$     write sys$output "     Home Directory: ", hdir
$     if (cdir .eqs. hdir) .and. (cnode .eqs. hnode) then goto DISPSKIP
$     write sys$output "       Current Node: ", cnode
$     write sys$output "  Current Directory: ", cdir
$  DISPSKIP:
$     write sys$output " "
$!
$  NODISPLAY:
$  ndir = f$environment("DEFAULT")
$  if .not. prompton then goto END
$!
$  if (f$length(ndir) .ge. 32) then goto TOOLONG
$!
$  SETPROMPT:
$  set prompt = 'ndir'" "
$!
$  END:
$  exit
$!
$  DIRERROR:
$  write sys$output " "
$  write sys$output "          ", ndir, " Directory does not exist!"
$  write sys$output " "
$  set default 'odir'
$  ndir = odir
$  goto NODISPLAY
$!
$! Prompt Problems------------------------------------------------------------
$!
$  TOOLONG:
$! Prompt is too long. Get rid of everything to the left of [ or <. If that
$! doesn't work, get rid of a subdirectory at a time.  As a last resort,
$! set the prompt back to $.
$!
$  left     = f$location("[", ndir)
$  len_ndir = f$length(ndir)
$  if (left .ge. len_ndir) then left = f$location("<",ndir)
$  if (left .gt. 0) .and. (left .lt. len_ndir) -
      then ndir = f$extract(left, len_ndir - left, ndir)
$!
$  STILLTOOLONG:
$    if (f$length(ndir) .lt. 32) then goto SETPROMPT
$    left     = f$location(".", ndir) + 1
$    len_ndir = f$length(ndir)
$    if left .ge. len_ndir then ndir = "$ "
$    if left .ne. len_ndir -
        then ndir = "[*" + f$extract(left, len_ndir - left, ndir)
$    goto STILLTOOLONG
$!
$! Wildcard Directory---------------------------------------------------------
$!
$  DIRSEARCH:
$  error_message = f$environment("MESSAGE")
$  on control_y then goto DIREND
$  on control_c then goto DIREND
$  set message/nosev/nofac/noid/notext
$  write sys$output " "
$  dispct = 1
$  dirct  = 0
$  pauseflag = 1
$!
$  DIRLOOP:
$    userfile = f$search("*.dir")
$    if (userfile .eqs. "") .and. (dirct .ne. 0) then goto DIRMENU
$    if (userfile .eqs. "") then goto DIRNONE
$    dispct = dispct + 1
$    dirct  = dirct  + 1
$    on severe then $ userprot = "No Priv"
$    userprot = f$file_attributes(userfile,"PRO")
$    if userprot .nes. "No Priv" then userprot = " "
$    userfile'dirct' = "[." + f$parse(userfile,,,"NAME") + "]"
$    userprot'dirct' = userprot
$    lengthflag = (f$length(userfile'dirct') .gt. 18)
$    if lengthflag then write sys$output -
        f$fao("  !3SL   !34AS  ", dirct, userfile'dirct'), userprot'dirct'
$    if (.not. lengthflag) then write sys$output -
        f$fao("  !3SL   !20AS  ", dirct, userfile'dirct'), userprot'dirct'
$    if (dispct .lt. 8) then goto DIRLOOP
$    dirct  = dirct  + 1
$    userfile'dirct' = ""
$    dirct  = dirct  + 1
$    userfile'dirct' = ""
$    if pauseflag then goto DIRMENU
$    dispct = 0
$    goto DIRLOOP
$!
$  DIRMENU:
$  write sys$output " "
$  if (userfile .eqs. "") then goto DIRMENU2
$     write sys$output "    M   More subdirectories"
$  if pauseflag then -
$     write sys$output "    N   More subdirectories/No pause"
$!
$  DIRMENU2:
$     write sys$output "    R   Re-Display subdirectories"
$     write sys$output "    Q   Quit (default)"
$
$  DIRINQUIRE:
$  write sys$output " "
$  inquire dirchoice "  Select One"
$  write sys$output " "
$!
$  if (dirchoice .gt. 0)    .and. -
      (dirchoice .le. dirct) then goto DIRCASEDIGIT
$  dirchoice = f$edit(dirchoice,"UPCASE")
$  if (dirchoice .eqs. "")  .or. -
      (dirchoice .eqs. "Q")  then goto DIRCASEBLANK
$  if (dirchoice .eqs. "M") .or. -
      (dirchoice .eqs. "N")  then goto DIRCASEMORE
$  if (dirchoice .eqs. "R")  then goto DIRCASERED
$!
$  DIRCASERROR:
$  if (dirct .eq. 1)   then write sys$output -
      "  Select 1 to change to the ", userfile1, " subdirectory. "
$  revdirct = dirct
$  if (dispct .eq. 8) then revdirct = revdirct - 2
$  if (dirct .gt. 1)   then write sys$output -
      "  Valid subdirectory selections are 1 through ", revdirct, " (Octal)."
$  goto DIRINQUIRE
$!
$  DIRCASEDIGIT:
$  if (userfile'dirchoice' .eqs. "") then goto DIRCASERROR
$  ndir = userfile'dirchoice'
$  goto DIREND
$!
$  DIRCASEBLANK:
$  write sys$output "  Subdirectory not changed."
$  write sys$output " "
$  goto DIREND
$!
$  DIRCASEMORE:
$  dispct = 0
$  if (dirchoice .eqs. "N") then pauseflag = 0
$  if (userfile .nes. "")   then goto DIRLOOP
$  write sys$output "  No more subdirectories to display."
$  goto DIRINQUIRE
$!
$  DIRCASERED:
$  dispct = 1
$  DISPLOOP:
$     if (userfile'dispct' .eqs "") then goto DISPDONT
$     lengthflag = (f$length(userfile'dispct') .gt. 18)
$     if lengthflag then write sys$output -
         f$fao("  !3SL   !34AS  ", dispct, userfile'dispct'), userprot'dispct'
$     if (.not. lengthflag) then write sys$output -
         f$fao("  !3SL   !20AS  ", dispct, userfile'dispct'), userprot'dispct'
$     DISPDONT:
$     dispct = dispct + 1
$     if (dispct .le. dirct) then goto DISPLOOP
$  goto DIRMENU
$!
$  DIRNONE:
$  write sys$output "No subdirectories to choose, or no directory privileges."
$  write sys$output " "
$  goto DIREND
$!
$  DIREND:
$  set message 'error_message'
$  on control_y then exit
$  on control_c then exit
$  if (ndir .eqs. "*") then goto DISPLAY
$  goto PARSE
$!
$!-Help-----------------------------------------------------------------------
$!
$  HELP:
$  type sys$input

               CD.COM  Version 6  VMS Change Directory Command

                         Usage:  CD command/directory

CD         Display home directory,       CD ..       Change directory to the
           current directory, node.      CD [-]      dir above current dir.

CD \       Change directory to your      CD ..sub    Change directory to a
CD HOME    SYS$LOGIN directory.          CD [-.sub]  "sideways" subdirectory.

CD dir     Change directory to the       CD *        Display/select the
CD [dir]   [dir] directory.                          available subdirectories.

CD .sub    Change directory to the       CD .        Reset current directory.
CD [.sub]  [.sub] subdirectory.          CD ?        Display CD instructions.

     CD :== @SYS$LOGIN:CD.COM                 DEFINE SYS$PROMPT "ON"
     To make CD available from                To have the VMS $ prompt
     any directory you change to.             display the current directory.

                              By The Mentor
$  goto END


                              Code for HUNT.COM
                              >>>>>>>>>>>>>>>>>


$ ! HUNT.COM
$ ! By The Mentor
$ ! Updated by: The Mad Mexican
$ ! Usage: SPAWN/NOWAIT @HUNT
$ !
$ !Searches SHOW USER output for a specified user,  strobes at given
$ !intervals considering the severity of the hunt at which time output
$ !is generated and process terminates. If user loggs in then output
$ !is generated and process terminates. May check both nodes if a set
$ !host is called.  Also supports a file with the names to be hunted for.
$ !
$ !  *** NOTE ***   This is set up for a two-node system with NYSSA
$ !                 being the default node and TEGAN being the alternate
$ !                 node (Circuit Breaker and some others will recognize
$ !                 the nodes as my 'home' ones.)  You will need to
$ !                 slightly modify the code to reflect the nodename(s)
$ !                 of whatever system you are using...
$ !
$ !
$ !
$ say="write sys$output"
$ on control then goto door
$ monitored_node = "''NODE'"
$ say "Monitoring node ''monitored_node'.  <HIT RETURN>"
$ severity_of_hunt:
$ inquire selection "Severity of HUNT, 1 being the most urgent: 1-2-3"
$ if selection.ge.2 then goto selection_2
$ delay="wait 00:00:20"
$ loop_count=40
$ goto begin_process
$ selection_2:
$ if selection.eq.3 then goto selection_3
$ delay="wait 00:01:00"
$ loop_count=8
$ goto begin_process
$ if selection.gt.3 then goto severity_of_hunt
$ delay="wait 00:02:30"
$ loop_count=20
$ begin_process:
$ if monitored_node.eqs."TEGAN" then goto search_file_tegan
$ if f$search("nyssa.dat9") .nes. "" then goto file_exist
$ goto continue
$ search_file_tegan:
$ if f$search("tegan.dat9") .nes. "" then goto file_exist
$ continue:
$ say "hit <RETURN>"
$ inquire/nopunctuate choice9 "Who are we hunting for? "
$ if choice9 .eqs. "" then exit
$ count = 0
$ bell_sound[0,8]=%X07
$ top:
$ sho user/output='monitored_node'.dat9
$ purge 'monitored_node'.dat9
$ set message/nofac/noid/notext/nosev
$ search 'monitored_node'.dat9 'choice9'
$ a=$severity
$ if a .eqs. "1" then goto found_user
$ set message 'temp_msg9'
$ count = count + 1
$ if count .ge. 'loop_count' then goto give_up
$ delay
$ goto top
$ file_exist:
$ say "ERROR - Could not create temporary data file."
$ say "Please delete or rename ''NODE'.DAT9"
$ exit
$ found_user:
$ say bell_sound
$ say "''choice9' is now online on node ''monitored_node'."
$ say bell_sound
$ goto door
$ give_up:
$ say " "
$ say "''choice9' has not yet logged in on ''monitored_node'."
$ door:
$ say bell_sound
$ say "HUNT routine has terminated on node ''monitored_node'."
$ delete/noconfirm/nolog 'monitored_node'.dat9;*
$ set message 'temp_msg9'
$ exit

                              Code for ALARM.COM
                              >>>>>>>>>>>>>>>>>>

$ ! ALARM.COM
$ ! By The Mentor
$ ! Usage: SPAWN/NOWAIT @ALARM
$ ! Strobes f$time() every 5 seconds until specified time
$ ! is met at which time output is generated and process terminates.
$ CLR = " "
$ count = 0
$ PID           = F$PID(CONTEXT)
$ TERMINAL      = F$GETJPI(''PID',"TERMINAL")
$ DEVICE        = F$GETDVI(TERMINAL,"DEVTYPE")
$ IF DEVICE .EQS. 110 THEN CLR = "[H[2J"  ! VT220
$ IF DEVICE .EQS.  98 THEN CLR = "[H[2J"  ! VT102
$ IF DEVICE .EQS.  96 THEN CLR = "[H[2J"  ! VT100
$ IF DEVICE .EQS.  64 THEN CLR = "HJ"     ! VT52
$ CLS = "WRITE SYS$OUTPUT CLR"
$ DATE       = F$CVTIME(F$TIME())
$ NODE       = F$GETSYI("NODENAME")
$ bell[0,8]=%X07
$ ON CONTROL THEN GOTO DOOR
$ say = "write sys$output"
$ say f$cvtime(,,"TIME")
$ say " "
$ say "Hit (RETURN)"
$ say " "
$ inquire/nopunctuate alarm "What time shall I ring you - "
$ a_hour = f$element(0,":",alarm)
$ a_minute = f$element(1,":",alarm)
$ a_second = f$element(2,":",alarm)
$ time_check:
$ hour = f$element(0,":",f$cvtime(,,"TIME"))
$ minute = f$element(1,":",f$cvtime(,,"TIME"))
$ second = f$element(2,":",f$element(0,".",f$cvtime(,,"TIME")))
$ if hour .ge. a_hour .and. minute .ge. a_minute .and. second .ge.
  a_second then goto top
$ if hour .ge. a_hour .and. minute .ge. a_minute then goto top
$ wait 00:00:05
$ goto time_check
$ top:
$ count = count + 1
$ cls
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say "                              A L A R M   O N"
$ say bell
$ say "                                 ",f$element(0,".",f$cvtime(,,"TIME"))
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ say " "
$ wait 00:00:01.50
$ if count .le. "6" then goto top
$ door:
$ say "ALARM OFF"
$ say f$element(0,".",f$cvtime(,,"TIME"))
$ say bell
$ exit


                               Code for CGO.COM
                               >>>>>>>>>>>>>>>>

$! CGO.COM
$! By The Mentor
$! One-Line compile/link/execute of C programs
$! Usage: CGO :== @CGO.COM
$!        CGO filename
$!
$if p1 .nes. "" then c_filename :== 'p1
$ write sys$output "Compiling:"
$ cc 'c_filename/list='c_filename.lst
$ write sys$output "Linking:"
$ link 'c_filename ,options_file/opt
$ write sys$output "Running:"
$ assign/user sys$command sys$input
$ run 'c_filename
$ exit
------------------------------------------------------------------------------

     Well, that's it.  I hope to be back in the next issue with some other
programs.  And remember, any programmers out there, get in touch with me!
                                  The Mentor
                              Thanksgiving 1987
==============================================================================
Received: (from LISTSERV@PSUVM for TK0EEE1@UCLAMAIL via NJE)
         (LISTSE00-7214;     368 LINES); Thu, 21 Dec 89 17:01:35 CST
Date:    Thu, 21 Dec 89 17:01 CST
To:      TK0EEE1
From:    LISTSERV@PSUVM


AOH Site layout & design copyright © 2006 AOH