Machine-Independent MDL for TOPS-20 and VAX.
[pdp10-muddle.git] / mim / development / mim / vax / mimlib / lup-base.mud
diff --git a/mim/development/mim/vax/mimlib/lup-base.mud b/mim/development/mim/vax/mimlib/lup-base.mud
new file mode 100644 (file)
index 0000000..868ce98
--- /dev/null
@@ -0,0 +1,855 @@
+;"*****************************************************************************
+  This file defines library update primitives for use with a resident library
+  file. Both the server and LUP use these procedures (spliced in at compile
+  time). See LIBRARY.FORMAT for definition of binary formats of record and
+  library.
+
+  LUP-BASE.MUD: EDIT HISTORY                               Machine Independent
+
+  COMPILATION: Spliced in at compile time.
+
+    JUN84 [Shane] - Created.
+  18OCT84 [Shane] - Commented, cleaned up.
+  28OCT84 [Shane] - Add LUPI-INSTALL.
+   9OCT84 [Shane] - Add LUPI-ADD-FILE, LUPI-FILE-EXISTS?, LUPI-DEL-FILE
+  ****************************************************************************"
+
+<COND (<NOT <VALID-TYPE? LIBLOCK>> <NEWTYPE LIBLOCK VECTOR>)>  ;"See L-DEFS."
+
+;"LUPI-KEY -- Is the LIBLOCK when an update is in progress."
+
+<OR <GASSIGNED? LUPI-KEY> <SETG LUPI-KEY %<> '<OR FALSE LIBLOCK>>>
+
+;"LUPI-ABORT --
+  Effect:   Aborts an update in progress. Deletes all temporary files
+           generated, writes message to log file, and unlocks library.
+  Modifies: K."
+
+<DEFINE LUPI-ABORT ("AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY)
+                         NAME:STRING CH:<CHANNEL 'DISK>)
+   <COND (.K
+         <MAPF %<> ,DELFILE <LL-TMP-FILES .K>>
+         <COND (<CHANNEL-OPEN? <SET CH <LL-LOG .K>>>
+                <PRINTSTRING "*** Update aborted." .CH>
+                <CRLF .CH>
+                <CLOSE .CH>)>
+         <COND (<CHANNEL-OPEN? <SET CH <LL-NEW .K>>>
+                <SET NAME <CHANNEL-OP .CH:DSK NAME>>
+                <CLOSE .CH>)
+               (T
+                <SET NAME <CHANNEL-NAME .CH>>)>
+         <DELFILE .NAME>
+         <IFSYS ("VAX"                     ;"Undo the soft lock under UNIX."
+                 <COND (<CHANNEL-OPEN? <SET CH <LL-OLD .K>>>
+                        <CALL SYSCALL FLOCK <CHANNEL-OP .CH:DSK FILE-HANDLE>
+                              ,UNLOCK-FILE>)>)>
+         <SETG LUPI-KEY %<>>
+         T)>>
+
+;"LUPI-GENTEMP --
+  Effect:   Generate a unique file name for the library directory:
+           LIBTMPnnn.TEMP if the library is locked.
+  Returns:  Filename.
+  Modifies: K."
+
+<DEFINE LUPI-GENTEMP ("AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND (.K
+         <PROG ((DEV:<SPECIAL STRING> <CHANNEL-OP <LL-OLD .K>:DSK DEV>)
+                (SNM:<SPECIAL STRING> <CHANNEL-OP <LL-OLD .K>:DSK SNM>)
+                (NM2:<SPECIAL STRING> "TEMP") (SUFFIX:FIX <LL-SUFFIX .K>)
+                (NAME:STRING <STACK <ISTRING 32>>)
+                FN:<CHANNEL 'PARSE>)
+            ;"Increment the suffix. I2S returns NAME rested to digits."
+            <LL-SUFFIX .K <+ .SUFFIX 1>>
+            <SUBSTRUC "LIBTMP" 0 6 <SET NAME <BACK <I2S .SUFFIX .NAME> 6>>>
+            <SET FN <CHANNEL-OPEN PARSE .NAME>>
+            <SET NAME <CHANNEL-OP .FN NAME>>
+            <CLOSE .FN>
+            <RETURN .NAME>>)>>
+\f
+;"LUPI-LOCK --
+  Effect:   Locks library corresponding to LIB. Creates the LIBLOCK and
+           writes message to LOG. The contents of the library is copied
+           and all update actions affect the copy.
+  Returns:  The locked active library.
+  Modifies: LOG.
+  Note:     The default for the log file is NM1.LOG where NM1 is first name
+           of library file in library directory."
+
+<DEFINE LUPI-LOCK (LIB:<CHANNEL 'DISK>
+                  "OPT" (LOG:<OR <CHANNEL 'DISK> FALSE> %<>)
+                  "AUX" (NEW:<OR <CHANNEL 'DISK> FALSE> %<>))
+   <PROG ((DEV:<SPECIAL STRING> <CHANNEL-OP .LIB DEV>)
+         (SNM:<SPECIAL STRING> <CHANNEL-OP .LIB SNM>)
+         (NM1:<SPECIAL STRING> <CHANNEL-OP .LIB NM1>)
+         (NM2:<SPECIAL STRING> <CHANNEL-OP .LIB NM2>)
+         (NAME:STRING <CHANNEL-OP .LIB NAME>)
+         FN:<CHANNEL 'PARSE>)
+      ;"Check to see if user already has library locked."
+      <COND (<AND <GASSIGNED? LUPI-KEY> ,LUPI-KEY>
+            <RETURN #FALSE ("LOCKED")>)>
+      <IFSYS ("VAX"
+             ;"Soft lock under UNIX."
+             <COND (<NOT <CALL SYSCALL FLOCK <CHANNEL-OP .LIB FILE-HANDLE>
+                               ,LOCK-FILE>>
+                    <RETURN #FALSE ("LOCKED")>)>)
+            ("TOPS20"
+             ;"Thawed under TOPS20."
+             <COND (<SET NEW <CHANNEL-OPEN DISK .NAME "MODIFY" "BINARY">>
+                    <CLOSE .LIB>
+                    <SET LIB .NEW>)
+                   (T
+                    <RETURN #FALSE ("LOCKED")>)>)>
+      ;"Open the log file if we were not given one. Recreate if needed."
+      <COND (<NOT .LOG>
+            <SET NM2 "LOG">
+            <SET FN <CHANNEL-OPEN PARSE .NM1>>
+            <SET NAME <CHANNEL-OP .FN NAME>>
+            <CLOSE .FN>
+            <COND (<SET LOG <CHANNEL-OPEN DISK .NAME "MODIFY" "ASCII">>
+                   <CHANNEL-OP .LOG ACCESS <FILE-LENGTH .LOG>>)
+                  (<SET LOG <CHANNEL-OPEN DISK .NAME "CREATE" "ASCII">>
+                   <CLOSE .LOG>
+                   <SET LOG <CHANNEL-OPEN DISK .NAME "MODIFY" "ASCII">>
+                   <PRINTSTRING "*** " .LOG>
+                   <PRINTSTRING <DTIME> .LOG>
+                   <PRINTSTRING " Log file recreated." .LOG>
+                   <CRLF .LOG>)>)>
+      ;"Copy the library to temporary file."
+      <SET NM2 "TEMP">
+      <SET FN <CHANNEL-OPEN PARSE "LIBTMP0">>
+      <SET NAME <CHANNEL-OP .FN NAME>>
+      <CLOSE .FN>
+      <COND (<SET NEW <CHANNEL-OPEN DISK .NAME "CREATE" "BINARY">>
+            <CHANNEL-OP .LIB:DSK ACCESS 0>
+            <REPEAT ((B:<UVECTOR [REST FIX]> <STACK <IUVECTOR 512>>) N:FIX)
+               <SET N <OR <CHANNEL-OP .LIB:DSK READ-BUFFER .B> 0>>
+               <CHANNEL-OP .NEW:DSK WRITE-BUFFER .B .N>
+               <COND (<L? .N <LENGTH .B>> <RETURN>)>>
+            ;"Make the key and say library is locked."
+            <SETG LUPI-KEY <CHTYPE [.LIB .NEW .LOG () () () 1] LIBLOCK>>
+            <PRINTSTRING "*** " .LOG>
+            <PRINTSTRING <DTIME> .LOG>
+            <PRINTSTRING " Library locked." .LOG>
+            <CRLF .LOG>
+            .LIB)>>>
+
+;"LUPI-COMMIT --
+  Effect:   Deletes files that have been deleted from library. Renames
+           temporary files to destinations for files that have been
+           added to library. Renames temporary library to library and
+           releases lock. All accompanied by messages to log file.
+  Modifies: K, LOG."
+
+<DEFINE LUPI-COMMIT ("AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY) LOG:<CHANNEL 'DISK>)
+   <COND (.K
+         <SET LOG <LL-LOG .K>>
+         ;"Delete files."
+         <MAPF %<>
+               <FUNCTION (NAME:STRING)
+                  <PRINTSTRING "    Deleting file " .LOG>
+                   <PRINTSTRING .NAME .LOG>
+                  <CRLF .LOG>
+                  <DELFILE .NAME>>
+               <LL-DEL-FILES .K>>
+         ;"Rename temporary files."
+         <MAPF %<>
+               <FUNCTION (FROM:STRING TO:STRING)
+                  <PRINTSTRING "    Adding file " .LOG>
+                  <PRINTSTRING .TO .LOG>
+                  <CRLF .LOG>
+                  <RENAME .FROM .TO>>
+               <LL-TMP-FILES .K>
+               <LL-ADD-FILES .K>>
+         ;"Rename temporary library, flushing old library."
+         <BIND ((OLD:STRING <CHANNEL-OP <LL-OLD .K>:DSK NAME>)
+                (NEW:STRING <CHANNEL-OP <LL-NEW .K>:DSK NAME>))
+            <CLOSE <LL-NEW .K>>
+            <CLOSE <LL-OLD .K>>
+            <IFSYS ("TOPS20" <DELFILE .OLD>)>
+            <RENAME .NEW .OLD>
+            <PRINTSTRING "*** Update completed." .LOG>
+            <CRLF .LOG>
+            <CLOSE .LOG>
+            <SETG LUPI-KEY %<>>
+            T>)>>
+\f
+;"LUPI-INSTALL --
+  Effect:   Deletes files that have been deleted from library. Renames
+           temporary files to destinations for files that have been
+           added to library. Renames temporary library to library but
+           retains lock. All accompanied by messages to log file.
+  Modifies: K, LOG."
+
+<DEFINE LUPI-INSTALL ("AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY) LOG:<CHANNEL 'DISK>)
+   <COND (.K
+         <SET LOG <LL-LOG .K>>
+         ;"Delete files."
+         <MAPF %<>
+               <FUNCTION (NAME:STRING)
+                  <PRINTSTRING "    Deleting file " .LOG>
+                  <PRINTSTRING .NAME .LOG>
+                  <CRLF .LOG>
+                  <DELFILE .NAME>>
+               <LL-DEL-FILES .K>>
+         ;"Rename temporary files."
+         <MAPF %<>
+               <FUNCTION (FROM:STRING TO:STRING)
+                  <PRINTSTRING "    Adding file " .LOG>
+                  <PRINTSTRING .TO .LOG>
+                  <CRLF .LOG>
+                  <RENAME .FROM .TO>>
+               <LL-TMP-FILES .K>
+               <LL-ADD-FILES .K>>
+         ;"Rename temporary library, flushing old library, locking new."
+         <BIND ((OLD:STRING <CHANNEL-OP <LL-OLD .K>:DSK NAME>)
+                (NEW:STRING <CHANNEL-OP <LL-NEW .K>:DSK NAME>)
+                CH:<OR <CHANNEL 'DISK> FALSE>)
+            <CLOSE <LL-NEW .K>>
+            <CLOSE <LL-OLD .K>>
+            <IFSYS ("TOPS20" <DELFILE .OLD>)>
+            <RENAME .NEW .OLD>
+            <PRINTSTRING "*** Installed." .LOG>
+            <CRLF .LOG>
+            <SETG LUPI-KEY %<>>
+            ;"Small window here where someone else could grab library."
+            <COND (<AND <SET CH <CHANNEL-OPEN DISK .OLD "READ" "BINARY">>
+                        <LUPI-LOCK .CH .LOG>>)
+                  (T
+                   <AND .CH <CLOSE .CH>>
+                   <CLOSE .LOG>
+                   %<>)>>)>>
+\f
+;"LUPI-CREATE --
+  Effect:  Create a library named NAME with default second name LIBMIM.
+          Creates associated log file. 
+  Returns: Full library name."
+
+<DEFINE LUPI-CREATE ("OPT" (NAME:STRING "LIBMIM") (NBKTS:FIX ,INITIAL-BUCKETS)
+                    "AUX" (LIB:<OR <CHANNEL 'DISK> FALSE> %<>)
+                          (LOG:<OR <CHANNEL 'DISK> FALSE> %<>))
+   <SET NBKTS <NEXT-PRIME .NBKTS>>
+   <PROG ((DIR:<UVECTOR [REST FIX]> <STACK <IUVECTOR <+ .NBKTS ,DIR-HDRLEN> 0>>)
+         (NM2:<SPECIAL STRING> "LBIN")
+         (FN:<CHANNEL 'PARSE> <CHANNEL-OPEN PARSE .NAME>))
+      <SET NAME <CHANNEL-OP .FN NAME>>
+      <CLOSE .FN>
+      ;"If it exists, ask user what to do."
+      <COND (<SET LIB <CHANNEL-OPEN DISK .NAME "READ" "BINARY">>
+            <COND (<ERROR LIBRARY-FILE-EXISTS!-ERRORS
+                          ERRET-T-TO-CLOBBER-EXISTING-LIBRARY!-ERRORS
+                          <SET NAME <CHNNEL-OP .LIB NAME>>
+                          LUPI-CREATE>
+                   <CLOSE .LIB>
+                   <DELFILE .NAME>)
+                  (T
+                   <RETURN %<>>)>)>
+      ;"Create library directory and log file."
+      <COND (<SET LIB <CHANNEL-OPEN DISK .NAME "CREATE" "BINARY">>
+            <PROG ((SNM:<SPECIAL STRING> <CHANNEL-OP .LIB SNM>)
+                   (DEV:<SPECIAL STRING> <CHANNEL-OP .LIB DEV>))
+               <SET NM2 "LOG">
+               <SET FN <CHANNEL-OPEN PARSE <CHANNEL-OP .LIB NM1>>>
+               <SET NAME <CHANNEL-OP .FN NAME>>
+               <CLOSE .FN>
+               <SET LOG <CHANNEL-OPEN DISK .NAME "CREATE" "ASCII">>>
+            ;"Hash table size and end of file pointer."
+            <PUT .DIR <+ ,DIR-TABSIZ 1> .NBKTS>
+            <PUT .DIR <+ ,DIR-EOFPTR 1> <LENGTH .DIR>>
+            <CHANNEL-OP .LIB WRITE-BUFFER .DIR>
+            <SET NAME <CHANNEL-OP .LIB NAME>>
+            <CLOSE .LIB>
+            <PRINTSTRING "*** " .LOG>
+            <PRINTSTRING <DTIME> .LOG>
+            <PRINTSTRING " Library created" .LOG>
+            <CRLF .LOG>
+            <CLOSE .LOG>
+            .NAME)
+           (T
+            <ERROR CANT-OPEN-LIBRARY-FILE!-ERRORS .NAME .LIB LUPI-CREATE>
+            .LIB)>>>
+\f
+;"LUPI-ADD-PACK --
+  Effect:   Adds RECORD to shadow library. The names in TMP are the names
+           of the temporary files involved in the transaction. The names
+           in ADD are the destination file names. The order MUST be the
+           same. Writes message to log file.
+  Modifies: K."
+
+<DEFINE LUPI-ADD-PACK (RECORD:<UVECTOR [REST FIX]> ADD:LIST TMP:LIST
+                      "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND (<AND .K
+              <==? <LENGTH .ADD> <LENGTH .TMP>>        ;"Just in case."
+              <ADD-RECORD .RECORD <LL-NEW .K>>>
+         <BIND ((LLADD:LIST <LL-ADD-FILES .K>)
+                (LLTMP:LIST <LL-TMP-FILES .K>)
+                (LLSIZE:FIX <LENGTH .LLTMP>)
+                (LOG:<CHANNEL 'DISK> <LL-LOG .K>)
+                (NAME:STRING <STACK <ISTRING ,MAXSTRS>>))
+            ;"Splice file names onto existing lists."
+            <COND (<==? .LLSIZE 0>
+                   <LL-TMP-FILES .K .TMP>
+                   <LL-ADD-FILES .K .ADD>)
+                  (<NOT <EMPTY? .TMP>>
+                   <PUTREST <REST .LLTMP <- .LLSIZE 1>> .TMP>
+                   <PUTREST <REST .LLADD <- .LLSIZE 1>> .ADD>)>
+            <PRINTSTRING "    Adding record " .LOG>
+            <PRINTSTRING .NAME .LOG
+                         <UV2SS <REST .RECORD> .NAME <BYTE0 <1 .RECORD>>>>
+            <CRLF .LOG>>)>>
+
+;"LUPI-ADD-FILE --
+  Effect:   Adds an auxiliary file to the library directory (such as a runtime
+           help file). TMP is the name of the temporary file obtained from
+           LUPI-GENTEMP and ADD is the name the file should be renamed to when
+           the library is installed.
+  Modifies: K."
+
+<DEFINE LUPI-ADD-FILE (TMP:STRING ADD:STRING
+                      "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND (.K
+         <LL-TMP-FILES .K (.TMP !<LL-TMP-FILES .K>)>
+         <LL-ADD-FILES .K (.ADD !<LL-ADD-FILES .K>)>
+         T)>>
+
+;"LUPI-DEL-FILE --
+  Effect:   Adds the name of an auxiliary file (in the library directory) to
+           the list of files to be deleted when the modified library is
+           installed.
+  Modifies: K."
+
+<DEFINE LUPI-DEL-FILE (DEL:STRING "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND (.K
+         <PROG ((OLD:<CHANNEL 'DISK> <LL-OLD .K>)
+                (DEV:<SPECIAL STRING> <CHANNEL-OP .OLD DEV>)
+                (SNM:<SPECIAL STRING> <CHANNEL-OP .OLD SNM>)
+                (FN:<CHANNEL 'PARSE> <CHANNEL-OPEN PARSE .DEL>)
+                (NM2:<SPECIAL STRING> <CHANNEL-OP .FN NM2>))
+            <SET DEL <CHANNEL-OP .FN NM1>>
+            <CLOSE .FN>
+            <CHANNEL-OPEN PARSE .DEL>
+            <SET DEL <CHANNEL-OP .FN NAME>>
+            <CLOSE .FN>
+            <LL-DEL-FILES .K (.DEL !<LL-DEL-FILES .K>)>
+            T>)>>
+\f
+;"LUPI-DEL-PACK --
+  Effect:   Deletes record named PACKAGE from shadow library and adds the
+           file names in record to delete list (if they correspond to files
+           in the library directory.
+  Modifies: K."
+
+<DEFINE LUPI-DEL-PACK (PACKAGE:STRING "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND
+    (.K
+     <PROG ((FILES:VECTOR <STACK <IVECTOR 4 %<>>>)
+           (LLDEL:LIST <LL-DEL-FILES .K>)
+           (LOG:<CHANNEL 'DISK> <LL-LOG .K>)
+           (NEW:<CHANNEL 'DISK> <LL-NEW .K>)
+           (DEV:<SPECIAL STRING> <CHANNEL-OP .NEW DEV>)
+           (SNM:<SPECIAL STRING> <CHANNEL-OP .NEW SNM>)
+           FN:<CHANNEL 'PARSE>)
+       <COND
+        (<REMOVE-RECORD .PACKAGE .NEW .FILES>
+         ;"Map over the files REMOVE-RECORD found, adding them to
+           delete list if they are in the library directory. This
+           will always happen unless the library contains explicit
+           path name (those without paths must be in the library
+           directory and will be parsed as such)."
+         <MAPF
+          %<>
+          <FUNCTION (NAME:<OR STRING FALSE>)
+             <COND (.NAME
+                    <SET FN <CHANNEL-OPEN PARSE .NAME>>
+                    <COND (<AND <=? <CHANNEL-OP .FN DEV>:STRING .DEV>
+                                <=? <CHANNEL-OP .FN SNM>:STRING .SNM>>
+                           <SET LLDEL
+                                (<CHANNEL-OP .FN NAME ,NO-GENERATION> !.LLDEL)>)>
+                    <CLOSE .FN>)>>
+          .FILES>
+         <LL-DEL-FILES .K .LLDEL>
+         <PRINTSTRING "    Removing record " .LOG>
+         <PRINTSTRING .PACKAGE .LOG>
+         <CRLF .LOG>
+         T)>>)>>
+
+;"LUPI-GC --
+  Effect:   Compact shadow library, reclaiming any holes in the file. The
+           old shadow library is closed and the garbage collected shadow
+           library becomes the shadow library. Writes message to log file.
+            NBKTS suggests the number of buckets to use.
+  Modifies: K."
+
+<DEFINE LUPI-GC ("OPT" (NBKTS:FIX ,INITIAL-BUCKETS)
+                 "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND (.K
+         <BIND ((NEW:<CHANNEL 'DISK> <LL-NEW .K>)
+                (NAME:STRING <LUPI-GENTEMP>)
+                (LOG:<CHANNEL 'DISK> <LL-LOG .K>)
+                NNEW:<OR FALSE <CHANNEL 'DISK>>)
+            <COND (<AND <SET NNEW <CHANNEL-OPEN DISK .NAME "CREATE" "BINARY">>
+                        <GC-LIB .NEW .NNEW .NBKTS>>
+                   <SET NAME <CHANNEL-OP .NEW NAME>>
+                   <FLUSH .NEW>
+                   <DELFILE .NAME>
+                   <LL-NEW .K .NNEW>
+                   <PRINTSTRING "*** Library GC" .LOG>
+                   <CRLF .LOG>
+                   T)
+                  (.NNEW
+                   <FLUSH .NNEW>
+                   <DELFILE .NAME>
+                   %<>)>>)>>
+\f
+;"NPRIME? -- Returns T iff N is not prime."
+
+<DEFINE NPRIME? (N:FIX)
+   #DECL ((N) FIX)
+   <REPEAT ((D:FIX 2) (SQ:FIX <FIX <+ <SQRT <FLOAT .N>>:FLOAT 1.0>>))
+      <COND (<G? .D .SQ> <RETURN %<>>)
+           (<==? <MOD .N .D> 0> <RETURN .D>)>
+      <SET D <+ .D 1>>>>
+
+;"NEXT-PRIME -- Returns next prime larger than X."
+
+<DEFINE NEXT-PRIME (X:FIX)
+   <REPEAT () <COND (<NOT <NPRIME? <SET X <+ .X 1>>>> <RETURN .X>)>>>
+
+;"ALLOCATE --
+  Effect:   Allocates AMT storage in LIB. The storage is taken from the free
+           list if possible otherwise storage is allocated at the end of
+           the file.
+  Modifies: LIB
+  Returns:  Address of allocated storage block."
+
+<DEFINE ALLOCATE (LIB:<CHANNEL 'DISK> AMT:FIX
+                 "AUX" BSIZE:FIX BADDR:FIX BPRED:FIX BSUCC:FIX SIZE:FIX
+                       ADDR:FIX SUCC:FIX PRED:FIX)
+   <SETADR .LIB <SET PRED ,DIR-FRELST>>     ;"Move to free list."
+   <SET ADDR <RDWRD .LIB>>                 ;"Address of first block."
+   <SET BADDR <RDWRD .LIB>>                ;"Best address initially EOF."
+   <SET BSIZE *77777777*>                  ;"Best size initially max."
+   <SET BPRED 0>                           ;"No predecessor."
+   <SET BSUCC 0>                           ;"No successor."
+   <COND (<N==? .ADDR 0>                   ;"Not end of list?"
+         <SETADR .LIB .ADDR>               ;"Go to block descriptor."
+         <SET SIZE <RDWRD .LIB>>           ;"Its size."
+         <SET SUCC <RDWRD .LIB>>           ;"Its cdr."
+         <REPEAT ()                        ;"Cruise the list."
+            <COND (<OR <==? .SIZE .AMT>    ;"Better tnan best so far?"
+                       ;"Must be at least 2 words bigger."
+                       <AND <G=? .SIZE <+ .AMT 2>> <L? .SIZE .BSIZE>>>
+                   <SET BSIZE .SIZE>       ;"Yep, set best variables."
+                   <SET BPRED .PRED>
+                   <SET BSUCC .SUCC>
+                   <SET BADDR .ADDR>)>
+            ;"If end of list or exact, we can do no better."
+            <COND (<OR <0? .SUCC> <==? .BSIZE .AMT>>
+                   <RETURN>)
+                  (T
+                   <SET PRED <+ .ADDR 1>>  ;"Move to next block."
+                   <SETADR .LIB <SET ADDR .SUCC>>
+                   <SET SIZE <RDWRD .LIB>>
+                   <SET SUCC <RDWRD .LIB>>)>>)>
+   <COND (<==? .BPRED 0>                   ;"0 -- eof, bump eof pointer."
+         <SETADR .LIB ,DIR-EOFPTR>
+         <WRWRD .LIB <+ .BADDR .AMT>>)
+        (T
+         <SETADR .LIB .BPRED>              ;"Got a block from list."
+         <COND (<==? .BSIZE .AMT>          ;"Exact, just splice out."
+                <WRWRD .LIB .BSUCC>)
+               (T                          ;"Carve out a piece."
+                <WRWRD .LIB <SET ADDR <+ .BADDR .AMT>>>
+                <SETADR .LIB .ADDR>        ;"Splice in reduced block."
+                <WRWRD .LIB <- .BSIZE .AMT>>
+                <WRWRD .LIB .BSUCC>)>)>
+   <COND (<G? <+ .BADDR .AMT> *77777777*>   ;"File address < 24 bits."
+         <ERROR LIBRARY-SPACE-EXHAUSTED!-ERRORS .BADDR ALLOCATE>
+         %<>)
+        (T
+         .BADDR)>>
+\f
+;"FREE --
+  Effect:   Deallocate AMT storage beginning at address START. The block
+           is spliced into the FREE list in storage order.
+  Modifies: LIB."
+
+<DEFINE FREE (LIB:<CHANNEL 'DISK> START:FIX AMT:FIX
+             "AUX" (END:FIX <+ .START .AMT>) (PAIR <STACK <UVECTOR .AMT 0>>)
+                   PRED:FIX SUCC:FIX)
+   <SETADR .LIB <SET PRED ,DIR-FRELST>>     ;"Move to free list."
+   <COND (<==? <SET SUCC <RDWRD .LIB>> 0>   ;"If none, its easy."
+         <SETADR .LIB ,DIR-FRELST>
+         <WRWRD .LIB .START>
+         <SETADR .LIB .START>
+         <WRBUF .LIB .PAIR>)
+        (T                                 ;"Find where block belongs."
+         <REPEAT (SIZE:FIX)
+            <SETADR .LIB .SUCC>            ;"Move to successor."
+            <RDBUF .LIB .PAIR>             ;"Get descriptor."
+            <COND (<==? .START <+ .SUCC <SET SIZE <1 .PAIR>>>>
+                   ;"Block is adjacent to end of SUCC, compact."
+                   <COND (<==? .END <2 .PAIR>>
+                          ;"Block exactly fills hole between SUCC and
+                            and its successor, compact all three."
+                          <SET AMT <+ .SIZE .AMT>>     ;"Add block to SUCC."
+                          <SETADR .LIB <2 .PAIR>>      ;"Move to SUCC's cdr."
+                          <RDBUF .LIB .PAIR>           ;"Get its size."
+                          <SET SIZE <1 .PAIR>>)>       ;"and its successor."
+                   <1 .PAIR <+ .SIZE .AMT>> ;"Add the words we are freeing."
+                   <SETADR .LIB .SUCC>      ;"Move to SUCC."
+                   <WRBUF .LIB .PAIR>       ;"And mung in the new descriptor."
+                   <RETURN>)
+                  (<==? .END .SUCC>
+                   ;"SUCC is adjacent to end of block, compact."
+                   <SETADR .LIB .START>
+                   ;"SUCC's successor becomes block's successor,
+                     add SUCC's size to block's size, mung in new descriptor
+                     and mung SUCC's predecessor.."
+                   <WRBUF .LIB <1 .PAIR <+ .SIZE .AMT>>>
+                   <SETADR .LIB .PRED>
+                   <WRWRD .LIB .START>
+                   <RETURN>)
+                  (<L? .START .SUCC>
+                   ;"Block belongs before SUCC, mung SUCC's predecessor and
+                     point block at SUCC."
+                   <SETADR .LIB .START>
+                   <WRBUF .LIB <1 <2 .PAIR .SUCC> .AMT>>
+                   <SETADR .LIB .PRED>
+                   <WRWRD .LIB .START>
+                   <RETURN>)
+                  (<==? <2 .PAIR> 0>
+                   ;"Block belongs after SUCC, point SUCC at block."
+                   <SETADR .LIB <+ .SUCC 1>>
+                   <WRWRD .LIB .START>
+                   <SETADR .LIB .START>
+                   <WRBUF .LIB <1 <2 .PAIR 0> .AMT>>
+                   <RETURN>)
+                  (T                        ;"Keep looking."
+                   <SET PRED <+ .SUCC 1>>
+                   <SET SUCC <2 .PAIR>>)>>)>
+   T>
+\f
+;"ADD-RECORD --
+  Effect:   Adds RECORD to LIB. The directory is pointed at RECORD and all of its
+           entrys.
+  Modifies: LIB
+  Requires: RECORD is a properly formatted library record as defined in
+           LIBRARY.FORMAT."
+
+<DEFINE ADD-RECORD (RECORD:<UVECTOR [REST FIX]> LIB:<CHANNEL 'DISK> "NAME" ADD-RECORD
+                   "AUX" TMP:FIX RECADDR:FIX ADDR:FIX ERCNT:FIX DELTA:FIX
+                         NBKTS:FIX 
+                         (STATS:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>))
+   <COND (<G? <SET TMP <LHALF <1 .RECORD>>> <LENGTH .RECORD>>
+         <ERROR BAD-RECORD!-ERRORS "Incorrect RECLEN" .TMP <LENGTH .RECORD>
+                ADD-RECORD>
+         <RETURN %<> .ADD-RECORD>)>
+   <SET RECADDR <ALLOCATE .LIB .TMP>>       ;"Get some space."
+   <SETADR .LIB .RECADDR>                   ;"Move to allocated address."
+   <WRBUF .LIB .RECORD .TMP>                ;"Write the record."
+   <SETADR .LIB ,DIR-TABSIZ>                ;"Get size of hash table."
+   <SET NBKTS <RDWRD .LIB>>
+   <SETADR .LIB ,DIR-LERCNT>                ;"Get entry, package counts."
+   <RDBUF .LIB .STATS>
+   <SET TMP <BYTE0 <1 .RECORD>>>            ;"Length of record name."
+   <SET ADDR <HASH-UV <REST .RECORD> .NBKTS .TMP>>
+   <ADD-POINTER .LIB .ADDR .RECADDR ,BKT-P>  ;"Point bucket at RECORD."
+   <SET TMP <NTH .RECORD <+ .TMP 3>>>       ;"Entry count, distance to list."
+   <SET ERCNT <RHALF .TMP>>
+   <SET DELTA <LHALF .TMP>>
+   <2 <1 .STATS <+ <1 .STATS> .ERCNT>> <+ <2 .STATS> 1>>
+   <SETADR .LIB ,DIR-LERCNT>                ;"Update statistics."
+   <WRBUF .LIB .STATS>
+   <REPEAT ()                               ;"Hash entrys, add pointers."
+      <COND (<0? .ERCNT> <RETURN>)>
+      <SET TMP <BYTE0 <NTH .RECORD <+ .DELTA 1>>>>
+      <SET ADDR <HASH-UV <REST .RECORD <+ .DELTA 1>> .NBKTS .TMP>>
+      <ADD-POINTER .LIB .ADDR <+ .RECADDR .DELTA> ,BKT-E>
+      <SET ERCNT <- .ERCNT 1>>
+      <COND (<G? <SET DELTA <+ .DELTA .TMP 1>> <LENGTH .RECORD>>
+            <ERROR BAD-RECORD!-ERRORS "Incorrect DELTAP" .DELTA
+                   <LENGTH .RECORD> ADD-RECORD>
+            <RETURN %<>>)>>>
+\f
+;"ADD-POINTER --
+  Effect:   Points the bucket FROM at address TO with MASK bits set in
+           bucket pointer (either BKT-P or BKT-E).
+  Modifies: LIB
+  Requires: FROM is address of BUCKET that was obtained from hasher, TO
+           is the address of a package or entry record, MASK is a legal
+           bucket mask."
+
+<DEFINE ADD-POINTER (LIB:<CHANNEL 'DISK> FROM:FIX TO:FIX MASK:FIX "AUX" TMP:FIX)
+   <SETADR .LIB .FROM>
+   <COND (<0? <SET TMP <RDWRD .LIB>>>       ;"Empty bucket."
+         <SETADR .LIB .FROM>
+         <WRWRD .LIB <ORB .TO .MASK>>)
+        (T                                  ;"Single or list."
+         <BIND ((PAIR:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>) ADDR:FIX)
+            <SET ADDR <ALLOCATE .LIB 2>>    ;"Get cons cell."
+            <COND (<TESTBIT .TMP ,BKT-M %<>>
+                   ;"It was single item. We have to get a cons cell for
+                     existing pointer and link FROM to the list of two
+                     items."
+                   <2 <1 .PAIR .TMP> <SET TMP <ALLOCATE .LIB 2>>>
+                   <SETADR .LIB .FROM>      ;"Point to list."
+                   <WRWRD .LIB <ORB ,BKT-M .ADDR>>
+                   <SETADR .LIB .ADDR>      ;"Old pointer."
+                   <WRBUF .LIB .PAIR>
+                   <2 <1 .PAIR <ORB .TO .MASK>> 0>
+                   <SETADR .LIB .TMP>       ;"New pointer."
+                   <WRBUF .LIB .PAIR>)
+                  (T                        ;"Cons onto existing list."
+                   <SETADR .LIB .FROM>
+                   <WRWRD .LIB <ORB ,BKT-M .ADDR>>
+                   <2 <1 .PAIR <ORB .MASK .TO>> <ADDRESS .TMP>>
+                   <SETADR .LIB .ADDR>
+                   <WRBUF .LIB .PAIR>)>>)>>
+\f
+;"REMOVE-RECORD --
+  Effect:   Removes record named PACKAGE from LIB. Removes all pointers to
+           record from directory, freeing the space. Gets names of files
+           associated with record.
+  Modifies: LIB, FILES
+  Returns:  Vector of file names associated with record."
+
+<DEFINE REMOVE-RECORD (PACKAGE:STRING LIB:<CHANNEL 'DISK>
+                      "OPT" (FILES:VECTOR <IVECTOR 4 %<>>)
+                      "AUX" (BUFFER:<UVECTOR [REST FIX]> <STACK <IUVECTOR ,MAXREC>>)
+                            (PDNLEN:FIX <LENGTHW .PACKAGE>)
+                            (STATS:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>)
+                            RECADDR:FIX NBKTS:FIX RINFO:FIX
+                      "NAME" REMOVE-RECORD)
+   <SET BUFFER <REST .BUFFER <- <LENGTH .BUFFER> .PDNLEN>>>
+   <SETADR .LIB ,DIR-TABSIZ>                ;"Get size of hash table."
+   <SET NBKTS <RDWRD .LIB>>
+   <SETADR .LIB ,DIR-LERCNT>                ;"Get package, entry count."
+   <RDBUF .LIB .STATS>
+   <BIND ((COMPARE:<UVECTOR [REST FIX]> <STACK <IUVECTOR .PDNLEN>>)
+         (BKT:FIX <HASH-SUV .PACKAGE .NBKTS .BUFFER>))
+      <SETADR .LIB .BKT>                    ;"Look for pointer to record."
+      <SET RECADDR <RDWRD .LIB>>
+      <COND (<TESTBIT .RECADDR ,BKT-P>
+            ;"Single package pointer. Move to record and compare names."
+            <SETADR .LIB <SET RECADDR <ADDRESS .RECADDR>>>
+            <SET RINFO <RDWRD .LIB>>        ;"Contains name length."
+            <COND (<==? <BYTE0 .RINFO> .PDNLEN>
+                   ;"Same length, is it the name?"
+                   <RDBUF .LIB .COMPARE>
+                   <COND (<N=? .COMPARE .BUFFER>
+                          <RETURN %<> .REMOVE-RECORD>)>)
+                  (T
+                   <RETURN %<> .REMOVE-RECORD>)>)
+           (<TESTBIT .RECADDR ,BKT-M>
+            ;"List, move through list examining package records."
+            <SETADR .LIB <ADDRESS .RECADDR>>
+            <REPEAT ((PAIR:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>) CDR:FIX)
+               <RDBUF .LIB .PAIR>
+               <SET RECADDR <1 .PAIR>>
+               <COND (<TESTBIT .RECADDR ,BKT-P>           ;"Package?"
+                      <SETADR .LIB <SET RECADDR <ADDRESS .RECADDR>>>
+                      <SET RINFO <RDWRD .LIB>>
+                      <COND (<==? <BYTE0 .RINFO> .PDNLEN> ;"Same name?"
+                             <RDBUF .LIB .COMPARE>
+                             <COND (<=? .COMPARE .BUFFER>
+                                    <RETURN>)>)>)>
+               <COND (<==? <SET CDR <2 .PAIR>> 0>         ;"Empty list?"
+                      <RETURN %<> .REMOVE-RECORD>)
+                     (T
+                      <SETADR .LIB .CDR>)>>)
+           (T
+            <RETURN %<> .REMOVE-RECORD>)>
+      <SET BUFFER <TOP .BUFFER>>            ;"Get the record."
+      <SETADR .LIB .RECADDR>
+      <RDBUF .LIB .BUFFER <LHALF .RINFO>>    ;"We got record info above."
+      <REMOVE-POINTER .LIB .BKT .RECADDR>    ;"Flush package pointer."
+      <FREE .LIB .RECADDR <LHALF .RINFO>>>   ;"Free the record space."
+\f
+   <BIND (ERCNT:FIX FSIZES:FIX DELTA:FIX)
+      <SET BUFFER <REST .BUFFER <+ .PDNLEN 1>>>
+      ;"Get sizes of file names."
+      <COND (<TESTBIT .RINFO ,RINFO-DFN?>    ;"Is there a doc file?"
+            <SET FSIZES <1 .BUFFER>>)
+           (T                               ;"No, mask out doc length."
+            <SET FSIZES <ANDB *77777777* <1 .BUFFER>>>)>
+      <SET ERCNT <RHALF <2 .BUFFER>>>       ;"Count of entries."
+      <SET DELTA <LHALF <2 .BUFFER>>>       ;"Distance to entries."
+      <SET BUFFER <REST .BUFFER 3>>
+      <MAPR %<>                                     ;"Get the file names."
+           <FUNCTION (FV:VECTOR "AUX" (FSIZE:FIX <BYTE0 .FSIZES>))
+              <SET FSIZES <LSH .FSIZES -8>>
+              <COND (<N==? .FSIZE 0>
+                     <1 .FV <UV2S .BUFFER .FSIZE>>
+                     <SET BUFFER <REST .BUFFER .FSIZE>>)
+                    (T
+                     <1 .FV %<>>)>>
+           .FILES>
+      ;"Update library package, entry counts."
+      <2 <1 .STATS <- <1 .STATS> .ERCNT>> <- <2 .STATS> 1>>
+      <SETADR .LIB ,DIR-LERCNT>
+      <WRBUF .LIB .STATS 2>
+      <SET BUFFER <REST <TOP .BUFFER> .DELTA>>
+      <REPEAT (ERLEN:FIX ADDR:FIX BKT:FIX)   ;"Remove pointers to entries."
+        <COND (<0? .ERCNT> <RETURN .FILES>)>
+        <SET ADDR <+ .RECADDR <- <LENGTH <TOP .BUFFER>> <LENGTH .BUFFER>>>>
+        <SET ERLEN <BYTE0 <1 .BUFFER>>>
+        <SET BKT <HASH-UV <REST .BUFFER> .NBKTS .ERLEN>>
+        <REMOVE-POINTER .LIB .BKT .ADDR>
+        <SET ERCNT <- .ERCNT 1>>
+        <SET BUFFER <REST .BUFFER <+ .ERLEN 1>>>>>>
+\f
+;"REMOVE-POINTER --
+  Effect:   Deletes pointer to ADDR found in BKT. If the pointer is in cons,
+           its storage is freed.
+  Modifies: LIB
+  Requires: BKT is the address of a bucket in the hash table, ADDR is present
+           in BKT."
+
+<DEFINE REMOVE-POINTER (LIB:<CHANNEL 'DISK> BKT:FIX ADDR:FIX "AUX" LAST:FIX)
+   <SETADR .LIB .BKT>
+   <COND (<==? <ADDRESS <SET LAST <RDWRD .LIB>>> .ADDR>
+         <SETADR .LIB .BKT>                 ;"ADDR was only thing in bucket"
+         <WRWRD .LIB 0>)
+        (<TESTBIT .LAST ,BKT-M>             ;"List?"
+         <SET LAST <ADDRESS .LAST>>         ;"Cruise the list."
+         <REPEAT ((PAIR:<UVECTOR [REST FIX]> <STACK <UVECTOR 0 0>>) (PRED:FIX .BKT))
+            <SETADR .LIB .LAST>             ;"Get a cons."
+            <RDBUF .LIB .PAIR>              ;"And check its car for ADDR."
+            <COND (<==? <ADDRESS <1 .PAIR>> .ADDR>
+                   <FREE .LIB .LAST 2>      ;"Give away the cons."
+                   <SET LAST <2 .PAIR>>     ;"Check out the cdr."
+                   <COND (<==? .LAST 0>     ;"Nil?"
+                          <SETADR .LIB .PRED>
+                          <WRWRD .LIB 0>    ;"Predecessor gets nil."
+                          <COND (<N==? .PRED .BKT>
+                                 ;"If this was last in list of two items, move
+                                   car of remaining cons to bucket."
+                                 <SETADR .LIB .BKT>
+                                 <SET LAST <ADDRESS <RDWRD .LIB>>>
+                                 <COND (<==? .LAST <SET PRED <- .PRED 1>>>
+                                        ;"Yep, bucket points to this cons."
+                                        <SETADR .LIB .PRED>
+                                        <SET LAST <RDWRD .LIB>>
+                                        <FREE .LIB .PRED 2>
+                                        <SETADR .LIB .BKT>
+                                        <WRWRD .LIB .LAST>)>)>)
+                         (<==? .PRED .BKT>
+                          ;"If this was first in list of two items, move
+                            car of remaining cons to bucket."
+                          <SETADR .LIB .LAST>
+                          <RDBUF .LIB .PAIR>
+                          <SETADR .LIB .BKT>
+                          <COND (<==? <2 .PAIR> 0>
+                                 ;"Yep, ADDR's successor is last in list."
+                                 <WRWRD .LIB <1 .PAIR>>
+                                 <FREE .LIB .LAST 2>)
+                                (T
+                                 <WRWRD .LIB <ORB .LAST ,BKT-M>>)>)
+                         (T                 ;"Just splice the cons out."
+                          <SETADR .LIB .PRED>
+                          <WRWRD .LIB .LAST>)>
+                   <RETURN>)
+                  (T
+                   <SET PRED <+ .LAST 1>>   ;"Cdr is second word."
+                   <SETADR .LIB <SET LAST <2 .PAIR>>>)>>)
+        (T
+         <ERROR BAD-POINTER!-ERRORS .LAST .ADDR REMOVE-POINTER>)>>
+\f
+;"GC-LIB --
+  Effect:   Copies contents of LIB to NEW in bucket order, rehashing if need.
+            NNBKTS suggests the number of buckets to use.
+  Modifies: NEW
+  Requires: LIB is properly formatted library as defined in LIBRARY.FORMAT"
+
+<DEFINE GC-LIB (LIB:<CHANNEL 'DISK> NEW:<CHANNEL 'DISK>
+                "OPT" (NNBKTS:FIX ,INITIAL-BUCKETS)
+                "NAME" GC-LIB 
+               "AUX" NBKTS:FIX TMP:FIX (PAIR:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>))
+   <SET NNBKTS <NEXT-PRIME .NNBKTS>>
+   <SETADR .LIB ,DIR-TABSIZ>                ;"Get table size."
+   <SET NBKTS <RDWRD .LIB>>
+   <SETADR .LIB ,DIR-LERCNT>                ;"Get package, entry counts."
+   <RDBUF .LIB .PAIR>
+   <COND (<G? </ <FLOAT <+ <1 .PAIR> <2 .PAIR>>> <FLOAT .NBKTS>> 1.5>
+         <SET NNBKTS                        ;"Rehash."
+               <MAX .NNBKTS
+                    <NEXT-PRIME <FIX <* 1.5 <FLOAT <+ <1 .PAIR> <2 .PAIR>>>>>>>>)>
+   ;"Construct and write new directory."
+   <BIND ((DIR:<UVECTOR [REST FIX]> <STACK <IUVECTOR <+ .NNBKTS ,DIR-HDRLEN> 0>>))
+      ;"Initialize table size, end of file pointer for new directory."
+      <PUT .DIR <+ ,DIR-TABSIZ 1> .NNBKTS>
+      <PUT .DIR <+ ,DIR-EOFPTR 1> <LENGTH .DIR>>
+      <SETADR .NEW ,DIR-TABSIZ>
+      <OR <WRBUF .NEW .DIR> <RETURN %<> .GC-LIB>>>
+   <SET NBKTS <+ .NBKTS ,DIR-HDRLEN>>
+   ;"Find all the records and copy them to NEW."
+   <REPEAT ((RECORD:<UVECTOR [REST FIX]> <STACK <IUVECTOR ,MAXREC>>) (NPKGS:FIX <2 .PAIR>)
+           (BKT:FIX ,DIR-HDRLEN))
+      <COND (<OR <==? .NPKGS 0> <G? .BKT .NBKTS>>
+            <RETURN .NEW>)>
+      <SETADR .LIB .BKT>                    ;"Get current bucket."
+      <SET TMP <RDWRD .LIB>>
+      <COND (<TESTBIT .TMP ,BKT-P>          ;"Package?"
+            <SETADR .LIB <ADDRESS .TMP>>
+            <RDBUF .LIB .RECORD 1>          ;"Record info."
+            <RDBUF .LIB <REST .RECORD> <- <LHALF <1 .RECORD>> 1>>
+            <OR <ADD-RECORD .RECORD .NEW> <RETURN %<> .GC-LIB>>
+            <SET NPKGS <- .NPKGS 1>>)
+           (<TESTBIT .TMP ,BKT-M>           ;"List?"
+            <REPEAT ()                      ;"Cruise the list."
+               <SETADR .LIB <ADDRESS .TMP>>
+               <RDBUF .LIB .PAIR>           ;"Get cons."
+               <COND (<TESTBIT <1 .PAIR> ,BKT-P>       ;"Package?"
+                      <SETADR .LIB <ADDRESS <1 .PAIR>>>
+                      <RDBUF .LIB .RECORD 1>   ;"Record info."
+                      <RDBUF .LIB
+                             <REST .RECORD>
+                             <- <LHALF <1 .RECORD>> 1>>
+                      <OR <ADD-RECORD .RECORD .NEW> <RETURN %<> .GC-LIB>>
+                      <SET NPKGS <- .NPKGS 1>>)>
+               <COND (<==? <SET TMP <2 .PAIR>> 0> <RETURN>)>>)>
+      <SET BKT <+ .BKT 1>>>>
+\f
+;"LUPI-RECORD-EXISTS? --
+  Effect:   Returns T if record named NAME exists in shadow library."
+
+<DEFINE LUPI-RECORD-EXISTS? (NAME:STRING
+                     "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY)
+                           (JUNK:<UVECTOR [REST FIX]> <STACK <IUVECTOR 2>>))
+   <AND .K <PACKAGE-POINTER .NAME <LL-NEW .K> .JUNK>>>
+
+;"LUPI-FILE-EXISTS? --
+  Effect:  Returns T if file named NAME is found in the library directory."
+
+<DEFINE LUPI-FILE-EXISTS? (NAME:STRING "AUX" (K:<OR FALSE LIBLOCK> ,LUPI-KEY))
+   <COND
+    (.K
+     <PROG ((FN:<CHANNEL 'PARSE> <CHANNEL-OPEN PARSE .NAME>)
+           (DEV:<SPECIAL STRING> <CHANNEL-OP <LL-OLD .K>:<CHANNEL 'DISK> DEV>)
+           (SNM:<SPECIAL STRING> <CHANNEL-OP <LL-OLD .K>:<CHANNEL 'DISK> SNM>)
+           (NM2:<SPECIAL STRING> <CHANNEL-OP .FN NM2>))
+       <SET NAME <CHANNEL-OP .FN NM1>>
+       <CLOSE .FN>
+       <RETURN <FILE-EXISTS? .NAME>>>)>>
+
+;"I2S --
+  Effect:   Convert I to string representation.
+  Modifies: S
+  Returns:  S rested to the first digit.
+  Requires: S is large enough to hold the representation of I."
+
+<DEFINE I2S (I:FIX "OPT" (S:STRING <ISTRING 13>)
+                   "AUX" (NEG:<OR ATOM FALSE> <L? .I 0>))
+   <COND (.NEG <SET I <- .I>>)>
+   <REPEAT ((P:FIX <LENGTH .S>) D:FIX)
+      <SET D <MOD .I 10>>
+      <PUT .S .P <CHTYPE <+ .D %<CHTYPE !\0 FIX>> CHARACTER>>
+      <SET P <- .P 1>>
+      <COND (<==? <SET I </ .I 10>> 0>
+             <COND (.NEG 
+                    <PUT .S .P !\->
+                    <SET P <- .P 1>>)>
+             <RETURN <REST .S .P>>)>>>
+
+