home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 2: PC / frozenfish_august_1995.bin / bbs / d09xx / d0904.lha / Fill / Source / Fill.e < prev   
Text File  |  1993-08-26  |  20KB  |  688 lines

  1. /*
  2.    Fill V1.1.  Smart Multi-file Mover.
  3.    Copyright ⌐1993 Barry Wills.  All rights reserved.
  4.    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5.    HISTORY:
  6.    ~~~~~~~
  7.    V0.10b - First release May 1993.
  8.    ~~~~~~
  9.    1.  Locks destination.  Doesn't care if it's a floppy.
  10.    2.  Locks source.  Source is always current directory.
  11.    3.  Examines contents of source directory.  Stores filenames and sizes in
  12.        a list in descending order.
  13.    4.  Checks free space on destination.  Gets from list largest files that
  14.        will fit on destination.  Moves files to destination.  Continues
  15.        until list is emptied or files remaining in list won't fit on an
  16.        empty volume.
  17.    5.  Prompts for disk-change when volume becomes full.
  18.    6.  Displays number of unused bytes on a finished volume.
  19.    7.  Supports options:
  20.        -b##  Copy buffer size (1-100k; default 20)
  21.        -c    Copy only.  Don't move files. (default MOVE FILES)
  22.        -e##  Error Margin for storage estimate (1-20 blocks; default 0)
  23.    8.  Preserves file attributes.
  24.    9.  Recovers from full disk error (untested.)
  25.  
  26.    V0.11b - Released (I forgot.)
  27.    ~~~~~~
  28.    1.  Corrected erroneous check for file too big to fit on empty volume.
  29.        V0.10b would keep asking for another disk, even though a file would
  30.        not fit on an empty volume.  User had to enter 'Q' or 'q' to quit at
  31.        the prompt.
  32.    2.  Corrected to get the destination infodata before displaying free
  33.        space when exiting the program.  Previously, the free space shown
  34.        upon exiting was the free space on the destination BEFORE the last
  35.        file was copied/moved.  (oops)
  36.  
  37.    V0.12b - Released 22 May 93.
  38.    ~~~~~~
  39.    1.  Added Ctrl-C abort capability.
  40.  
  41.    V1.0 - Released 13 Jun 93.
  42.    ~~~~
  43.    1.  Removed from beta status.
  44.    2.  New command-line argument for source dev:directory.
  45.    3.  Now using arp.library to select filenames by pattern.
  46.  
  47.    V1.1 - Released 24 Jul 93.
  48.    ~~~~
  49.    1.  Added -n switch for "no overhead consideration", intended for use
  50.        with MS-DOS floppies.
  51.    2.  Cosmetic adjustment of status messages.
  52.    3.  If a destination directory is specified, the file name was prepended
  53.        with the directory name instead of being placed in the destination
  54.        directory.
  55.  
  56.  
  57.  
  58. */
  59.  
  60. MODULE 'dos/dos'
  61. MODULE 'libraries/arpbase'
  62. MODULE 'arp'
  63.  
  64.  
  65. PMODULE 'PMODULES:commandLineArgs'
  66. PMODULE 'PMODULES:upperChar'
  67.  
  68.  
  69.  
  70. /* Runtime exceptions. */
  71. ENUM ER_NONE,
  72.      ER_OPEN_ARPLIBRARY,
  73.      ER_USAGE,
  74.      ER_DEST_LOCK,
  75.      ER_SOURCE_SPEC,
  76.      ER_DEST_INFO,
  77.      ER_FILES_TOO_LARGE,
  78.      ER_MEM,
  79.      ER_WONT_FIT,
  80.      ER_USER_ABORT
  81.  
  82.  
  83. RAISE ER_MEM IF New () = NIL,
  84.       ER_MEM IF String () = NIL
  85.  
  86.  
  87. DEF copyBuffer  /* Will be allocated. */
  88.  
  89.  
  90. CONST SIZEOF_A_FILE_BLOCK = 35136,
  91.       NUMBLOCKS_USED_ON_BLANK_DISK_AMIGA_880K = 2,
  92.       NUMBLOCKS_USED_ON_BLANK_DISK_IBM_720K = 14
  93.  
  94.  
  95.  
  96. /*=== List definitions. ==================================================*/
  97.  
  98. /* NOTE:  I did this stuff before I tried playing with E lists.  It works */
  99. /* so I won't try changing it until the next release.                     */
  100.  
  101. OBJECT fl_ElementType
  102.   fileName       : LONG  /*  ptr to string  */
  103.   fileSize       : LONG  /*  long int       */
  104.   fileProtection : LONG  /*  long int       */
  105. ENDOBJECT
  106.     /* fl_ElementType */
  107.  
  108.  
  109. OBJECT fl_NodeType
  110.   element  : LONG  /*  ptr to fl_ElementType  */
  111.   nextNode : LONG  /*  ptr to fl_NodeType     */
  112. ENDOBJECT
  113.     /* fl_NodeType */
  114.  
  115.  
  116. OBJECT fl_ListType
  117.   head    : LONG  /*  all ptr to fl_NodeType  */
  118.   tail    : LONG
  119.   current : LONG
  120. ENDOBJECT
  121.     /* fl_ListType */
  122.  
  123.  
  124.  
  125. /*=== Command-line argument defs. ========================================*/
  126.  
  127. CONST MAX_ARG_BUFSIZE = 100,
  128.       MAX_ARG_ERRORMARGIN = 20,
  129.       MAX_DEPTH_OF_COMPARISON = 2
  130.  
  131.  
  132.  
  133. DEF optionIsSet_CopyOnly = FALSE,
  134.     argBufSize = 20,
  135.     argErrorMargin = 0,
  136.     argSourceSpec = NIL,
  137.     argDestPath = NIL,
  138.     argNoDosOverHead = FALSE,
  139.     sourceDir [108] : STRING,
  140.     sourcePathAndFilename [108] : STRING,
  141.     destPathAndFilename [108] : STRING,
  142.     numblocksUsedOnABlankDisk = NUMBLOCKS_USED_ON_BLANK_DISK_AMIGA_880K,
  143.     userAbort = FALSE
  144.  
  145.  
  146.  
  147. /*=== Command-line Argument Parser =======================================*/
  148.  
  149. PROC getSourceDir ()
  150.   DEF c
  151.   c := StrLen (argSourceSpec)
  152.   WHILE (c-- > 0) AND
  153.         (argSourceSpec [c] <> ":") AND
  154.         (argSourceSpec [c] <> "/") DO NOP
  155.   IF c = 0
  156.     StrCopy (sourceDir, '', ALL)
  157.   ELSE
  158.     INC c
  159.     WHILE c-- >= 0 DO sourceDir [c] := argSourceSpec [c]
  160.     SetStr (sourceDir, StrLen (sourceDir))
  161.   ENDIF
  162. ENDPROC
  163.  
  164.  
  165.  
  166. PROC parseCommandLineArguments () HANDLE
  167.   DEF index = 1,
  168.       char,
  169.       theArg : PTR TO CHAR
  170.  
  171.   theArg := String (StrLen (arg))
  172.  
  173.   WHILE getArg (theArg, index)
  174.     INC index
  175.     char := theArg [0]
  176.  
  177.     IF char = "-"
  178.       char := theArg [1]
  179.       SELECT char
  180.         CASE "c"
  181.           optionIsSet_CopyOnly := TRUE
  182.         CASE "b"
  183.           /* Use nextArg to save storage. */
  184.           /* MidStr (nextArg, theArg, 2, ALL) */
  185.           argBufSize := Val (/*nextArg*/ (theArg+2), NIL)
  186.           IF argBufSize <=0 THEN Raise (ER_USAGE)
  187.           IF argBufSize > MAX_ARG_BUFSIZE THEN argBufSize := MAX_ARG_BUFSIZE
  188.         CASE "e"
  189.           /* Use nextArg to save storage. */
  190.           /* MidStr (nextArg, theArg, 2, ALL) */
  191.           argErrorMargin := Val (/*nextArg*/ (theArg+2), NIL)
  192.           IF argErrorMargin <=0 THEN Raise (ER_USAGE)
  193.           IF argErrorMargin > MAX_ARG_ERRORMARGIN THEN argErrorMargin := MAX_ARG_ERRORMARGIN
  194.         CASE "n"
  195.           argNoDosOverHead := TRUE
  196.           numblocksUsedOnABlankDisk := NUMBLOCKS_USED_ON_BLANK_DISK_IBM_720K
  197.       ENDSELECT
  198.     ELSEIF argSourceSpec = NIL
  199.       argSourceSpec := String (StrLen (theArg))
  200.       StrCopy (argSourceSpec, theArg, ALL)
  201.     ELSEIF argDestPath = NIL
  202.       argDestPath := String (StrLen (theArg))
  203.       StrCopy (argDestPath, theArg, ALL)
  204.     ELSE  /* Too many args. */
  205.       Raise (ER_USAGE)
  206.     ENDIF
  207.   ENDWHILE
  208.  
  209.   IF (argSourceSpec = NIL) OR
  210.      (argDestPath = NIL) THEN Raise (ER_USAGE)
  211.  
  212.   Dispose (theArg)
  213.  
  214.   getSourceDir ()
  215.  
  216. EXCEPT
  217.  
  218.   Raise (exception)
  219.  
  220. ENDPROC
  221.   /* parseCommandLineArguments */
  222.  
  223.  
  224.  
  225. /*=== Begin File List Implementation =====================================*/
  226.  
  227. /*------------------------------------------------------------------------
  228.    These functions are used to gain easy access to the list substructures.
  229. --------------------------------------------------------------------------*/
  230.  
  231. PROC fl_FileSizeFrom (theElement)
  232.   DEF el : PTR TO fl_ElementType
  233.   el := theElement
  234. ENDPROC el.fileSize  /* long int */
  235.  
  236.  
  237.  
  238. PROC fl_ElementFrom (theNode)
  239.   DEF node : PTR TO fl_NodeType
  240.   node := theNode
  241. ENDPROC node.element  /* ptr to fl_ElementType */
  242.  
  243.  
  244.  
  245. PROC fl_NextNodeFrom (theNode)
  246.   DEF node : PTR TO fl_NodeType
  247.   node := theNode
  248. ENDPROC node.nextNode  /* ptr to fl_ElementType */
  249.  
  250.  
  251.  
  252. /*------------------------------------------------------------------------
  253.    These functions are used to manipulate the list.
  254. --------------------------------------------------------------------------*/
  255.  
  256. PROC fl_New ()
  257.   DEF newFileList : PTR TO fl_ListType,
  258.       head : PTR TO fl_NodeType,
  259.       tail : PTR TO fl_NodeType
  260.  
  261.   newFileList := New (SIZEOF fl_ListType)
  262.  
  263.   newFileList.head := New (SIZEOF fl_NodeType)
  264.   newFileList.tail := New (SIZEOF fl_NodeType)
  265.  
  266.   head := newFileList.head
  267.   tail := newFileList.tail
  268.   head.nextNode := newFileList.tail
  269.   tail.nextNode := NIL
  270.   head.element := NIL
  271.   tail.element := NIL
  272.  
  273.   newFileList.current := newFileList.head
  274.  
  275. ENDPROC newFileList  /* fl_ListType */
  276.   /* fl_New */
  277.  
  278.  
  279.  
  280. PROC fl_Insert (theElement, theList)
  281.   DEF newNode : PTR TO fl_NodeType,
  282.       element : PTR TO fl_ElementType,
  283.       list : PTR TO fl_ListType,
  284.       current : PTR TO fl_NodeType,
  285.       newElement : PTR TO fl_ElementType
  286.  
  287.   element := theElement
  288.   list := theList
  289.  
  290.   list.current := list.head
  291.   WHILE (fl_NextNodeFrom (list.current) <> list.tail) AND
  292.         fl_IsLessThan (element, fl_ElementFrom (fl_NextNodeFrom (list.current)))
  293.     list.current := fl_NextNodeFrom (list.current)
  294.   ENDWHILE
  295.  
  296.   current := list.current  /* shorten name to get at substructure */
  297.  
  298.   newNode := New (SIZEOF fl_NodeType)
  299.  
  300.   newNode.element := New (SIZEOF fl_ElementType)
  301.  
  302.   newElement := newNode.element
  303.   newElement.fileName := element.fileName
  304.   newElement.fileSize := element.fileSize
  305.   newElement.fileProtection := element.fileProtection
  306.   element.fileName := NIL  /* detach pointer so that list owns it */
  307.  
  308.   newNode.nextNode := current.nextNode
  309.   current.nextNode := newNode
  310.  
  311. ENDPROC TRUE
  312.   /* fl_Insert */
  313.  
  314.  
  315.  
  316. PROC fl_RetrieveFirst (theList)
  317.   DEF list : PTR TO fl_ListType
  318.   IF fl_IsEmpty (theList) THEN RETURN NIL
  319.   list := theList
  320.   list.current := fl_NextNodeFrom (list.head)
  321.   RETURN fl_ElementFrom (list.current)
  322. ENDPROC
  323.   /* fl_RetrieveFirst */
  324.  
  325.  
  326.  
  327. PROC fl_RetrieveNext (theList)
  328.   DEF list : PTR TO fl_ListType
  329.   IF fl_IsEmpty (theList) THEN RETURN NIL
  330.   list := theList
  331.   IF fl_NextNodeFrom (list.current) = list.tail THEN RETURN NIL
  332.   list.current := fl_NextNodeFrom (list.current)
  333.   RETURN fl_ElementFrom (list.current)
  334. ENDPROC
  335.   /* fl_RetrieveNext */
  336.  
  337.  
  338.  
  339. PROC fl_RemoveCurrent (theList)
  340.   DEF list : PTR TO fl_ListType,
  341.       current : PTR TO fl_NodeType,
  342.       node : PTR TO fl_NodeType,
  343.       element : PTR TO fl_ElementType
  344.  
  345.   IF fl_IsEmpty (theList) THEN RETURN NIL
  346.  
  347.   list := theList
  348.  
  349.   /* find node */
  350.   IF list.current = list.head THEN RETURN NIL
  351.      /* current undefined; must call one   */
  352.      /* of the functions that set current. */
  353.   IF list.current = list.tail THEN RETURN NIL
  354.      /* At end of list. */
  355.   current := list.head
  356.   WHILE (current.nextNode <> list.current)
  357.     current := current.nextNode
  358.   ENDWHILE
  359.  
  360.   /* detach node */
  361.   node := list.current
  362.   current.nextNode := node.nextNode
  363.   list.current := current
  364.      /* this sets up for a possible subsequent call to fl_RetrieveNext. */
  365.  
  366.   /* remove element and deallocate node */
  367.   element := node.element
  368.   Dispose (node)
  369.  
  370.   RETURN element
  371.  
  372. ENDPROC
  373.   /* fl_RemoveCurrent */
  374.  
  375.  
  376.  
  377. PROC fl_IsLessThan (thisElement, thatElement)
  378.   RETURN fl_FileSizeFrom (thisElement) < fl_FileSizeFrom (thatElement)
  379. ENDPROC
  380.  
  381.  
  382.  
  383. PROC fl_IsEmpty (theList)
  384.   DEF list : PTR TO fl_ListType
  385.   list := theList
  386.   RETURN fl_NextNodeFrom (list.head) = list.tail
  387. ENDPROC
  388.  
  389.  
  390. /*=== End File List Implementation =======================================*/
  391.  
  392.  
  393.  
  394. PROC enoughRoomOnDest (theDestInfo : PTR TO infodata,
  395.                        theElement : PTR TO fl_ElementType)
  396.   DEF numBytesFree,
  397.       numBytesRequired,
  398.       numFileExtensionBlocks,
  399.       numBytesForFileExtensionBlocks
  400.  
  401.   IF theElement = NIL THEN RETURN FALSE
  402.  
  403.   /* Compute what DOS says is free. */
  404.   numBytesFree := Mul ((theDestInfo.numblocks - theDestInfo.numblocksused),
  405.                        theDestInfo.bytesperblock)
  406.  
  407.   IF argNoDosOverHead
  408.     numBytesRequired := theElement.fileSize
  409.   ELSE
  410.     /*------------------------------------------------*/
  411.     /* Storage required by DOS filesystem =           */
  412.     /*   file_size_in_bytes +                         */
  413.     /*   one_block_for_file_header +                  */
  414.     /*   number_file_extension_blocks_required *      */
  415.     /*    bytes_per_block                             */
  416.     /*------------------------------------------------*/
  417.     numFileExtensionBlocks := Div (theElement.fileSize, SIZEOF_A_FILE_BLOCK)
  418.  
  419.     numBytesForFileExtensionBlocks := Mul (numFileExtensionBlocks,
  420.                                            theDestInfo.bytesperblock)
  421.     numBytesRequired := theElement.fileSize +            /* file size        */
  422.                         theDestInfo.bytesperblock +      /* file header      */
  423.                         numBytesForFileExtensionBlocks + /* extension blocks */
  424.                         (argErrorMargin *
  425.                          theDestInfo.bytesperblock)
  426.   ENDIF
  427.  
  428. /*** LEAVE THESE IN JUST IN CASE SOMEONE REPORTS ERRORS ********************
  429. WriteF ('\n\nfilename                      \s', theElement.fileName)
  430. WriteF ('\n filesize                       \d', theElement.fileSize)
  431. WriteF ('\n numblocks                      \d', theDestInfo.numblocks)
  432. WriteF ('\n numblocksused                  \d', theDestInfo.numblocksused)
  433. WriteF ('\n bytesperblock                  \d', theDestInfo.bytesperblock)
  434. WriteF ('\n numbytesfree                   \d', numBytesFree)
  435. WriteF ('\n numFileExtensionBlocks         \d', numFileExtensionBlocks)
  436. WriteF ('\n numBytesForFileExtensionBlocks \d', numBytesForFileExtensionBlocks)
  437. WriteF ('\n numBytesRequired               \d', numBytesRequired)
  438. Raise (0)
  439. ***************************************************************************/
  440.  
  441. ENDPROC  numBytesRequired <= numBytesFree
  442.   /* enoughRoomOnDest */
  443.  
  444.  
  445.  
  446. PROC moveFile (theElement : PTR TO fl_ElementType) HANDLE
  447.   DEF sourceFileHandle = NIL,
  448.       destFileHandle = NIL,
  449.       bytesRead,
  450.       bytesWritten = 1
  451.  
  452.   StringF (sourcePathAndFilename, '\s\s',
  453.            sourceDir, theElement.fileName)
  454.   StringF (destPathAndFilename, '\s\s\s',
  455.            argDestPath,
  456.            IF Char (argDestPath + StrLen (argDestPath) - 1) = ":" THEN '' ELSE '/',
  457.            theElement.fileName)
  458.  
  459.   WriteF ('\n   \s \s ...',
  460.           IF optionIsSet_CopyOnly THEN 'Copying' ELSE 'Moving',
  461.           sourcePathAndFilename)
  462.  
  463.   sourceFileHandle := Open (sourcePathAndFilename, OLDFILE)
  464.   IF sourceFileHandle = NIL THEN Raise ('Error opening file for input.')
  465.  
  466.   destFileHandle := Open (destPathAndFilename, NEWFILE)
  467.   IF destFileHandle = NIL THEN Raise ('Error opening file for output.')
  468.  
  469.   REPEAT
  470.     bytesRead := Read (sourceFileHandle, copyBuffer, argBufSize)
  471.     IF bytesRead > 0
  472.       bytesWritten := Write (destFileHandle, copyBuffer, bytesRead)
  473.       IF bytesWritten <> bytesRead THEN Raise (ER_WONT_FIT)
  474.     ENDIF
  475.     userAbort := CtrlC ()
  476.   UNTIL (bytesRead < 1) OR
  477.         (bytesWritten < 1) OR
  478.         userAbort
  479.  
  480.   Close (destFileHandle)
  481.   Close (sourceFileHandle)
  482.  
  483.   /* Check for IO error. */
  484.   IF bytesRead < 0 THEN Raise ('Error reading input file.')
  485.   IF bytesWritten < 0 THEN Raise ('Error writing output file.')
  486. IF userAbort = FALSE
  487.   IF optionIsSet_CopyOnly
  488.     WriteF (' copied.')
  489.   ELSE
  490.     DeleteFile (sourcePathAndFilename)
  491.     WriteF (' moved.')
  492.   ENDIF
  493.  
  494.   IF SetProtection (destPathAndFilename, theElement.fileProtection) = FALSE
  495.     WriteF ('\nFailed to set protection bits for \s.', theElement.fileName)
  496.   ENDIF
  497. ELSE
  498.   DeleteFile (destPathAndFilename)
  499. ENDIF
  500. EXCEPT
  501.  
  502.   IF sourceFileHandle THEN Close (sourceFileHandle)
  503.   IF destFileHandle THEN Close (destFileHandle)
  504.  
  505.   SELECT exception
  506.     CASE ER_WONT_FIT
  507.       WriteF ('\n didn\at fit.  Trying a smaller one.')
  508.       DeleteFile (destPathAndFilename)
  509.       RETURN FALSE
  510.     DEFAULT
  511.       WriteF ('\n\n\s', exception)
  512.   ENDSELECT
  513.  
  514.   Raise (exception)
  515.  
  516. ENDPROC TRUE
  517.   /* moveFile */
  518.  
  519.  
  520.  
  521. PROC main ()  HANDLE
  522.   DEF anchorPath : anchorpath,  /* Arp object. */
  523.       sourceFib : fileinfoblock,
  524.       destLock = NIL,
  525.       destInfo : infodata,
  526.       fileList : PTR TO fl_ListType,
  527.       element : PTR TO fl_ElementType,
  528.       sourceFindSuccess,
  529.       destInfoSuccess,
  530.       fileName,
  531.       checkingForFile,
  532.       char,
  533.       newDisk = TRUE,
  534.       filesMoved = 0
  535.  
  536.   WriteF ('\n   Fill V1.1.  Smart Multi-file Mover/Copier.' +
  537.           '\n   Copyright ⌐1993 Barry Wills.  All rights reserved.')
  538.  
  539.   /* OPEN ARP LIBRARY. */
  540.  
  541.   IF (arpbase := OpenLibrary ('arp.library', 39)) = NIL
  542.     Raise (ER_OPEN_ARPLIBRARY)
  543.   ENDIF
  544.  
  545.   /* GET COMMAND LINE ARGUMENTS. */
  546.  
  547.   IF arg [] = 0 THEN Raise (ER_USAGE)
  548.  
  549.   copyBuffer := New ((argBufSize * 1024))
  550.   fileList := fl_New ()
  551.   parseCommandLineArguments ()
  552.  
  553.   /* CHECK DESTINATION VALIDITY. */
  554.  
  555.   IF (destLock := Lock (argDestPath, SHARED_LOCK)) = NIL THEN Raise (ER_DEST_LOCK)
  556.  
  557.   /* CHECK SOURCE VALIDITY. */
  558.  
  559.   anchorPath.breakbits := SIGBREAKF_CTRL_C  /* Arp: allow user to abort. */
  560.   anchorPath.strlen := 0
  561.   IF (sourceFindSuccess := FindFirst (argSourceSpec, anchorPath)) <> 0
  562.     Raise (ER_SOURCE_SPEC)
  563.   ENDIF
  564.  
  565.   /* GET SOURCE FILE LIST. */
  566.  
  567.   WriteF ('\n\n   Getting file list.')
  568.  
  569.   /* Put filenames and sizes in a list, */
  570.   /* sorted on filesize by fl_Insert(). */
  571.   WHILE sourceFindSuccess = 0
  572.     sourceFib := anchorPath.info
  573.     IF sourceFib.direntrytype < 0
  574.       fileName := String (108)
  575.       StrCopy (fileName, sourceFib.filename, ALL)
  576.       fl_Insert ([fileName, sourceFib.size, sourceFib.protection], fileList)
  577.     ENDIF
  578.     sourceFindSuccess := FindNext (anchorPath)
  579.   ENDWHILE
  580.  
  581.   /* FINISHED WITH ARP. */
  582.  
  583.   FreeAnchorChain (anchorPath); anchorPath := NIL
  584.   CloseLibrary (arpbase); arpbase := NIL
  585.  
  586.   /* MOVE FILES. */
  587.  
  588.   WHILE fl_IsEmpty (fileList) = FALSE
  589.     IF newDisk OR (element = NIL)
  590.       element := fl_RetrieveFirst (fileList)
  591.       newDisk := FALSE
  592.       filesMoved := 0
  593.       WriteF ('\n')
  594.     ELSE
  595.         element := fl_RetrieveNext (fileList)
  596.     ENDIF
  597.  
  598.     destInfoSuccess := Info (destLock, destInfo)
  599.     IF destInfoSuccess = FALSE THEN Raise (ER_DEST_INFO)
  600.  
  601.     checkingForFile := TRUE
  602.     WHILE checkingForFile
  603.       IF element = NIL
  604.         checkingForFile := FALSE
  605.       ELSEIF enoughRoomOnDest (destInfo, element)
  606.         checkingForFile := FALSE
  607.       ELSE
  608.         element := fl_RetrieveNext (fileList)
  609.       ENDIF
  610.     ENDWHILE
  611.  
  612.     IF element <> NIL
  613.       element := fl_RemoveCurrent (fileList)
  614.       moveFile (element)
  615.       IF userAbort THEN Raise (ER_USER_ABORT)
  616.       INC filesMoved
  617.       Dispose (element)
  618.     ELSEIF destInfo.numblocksused = numblocksUsedOnABlankDisk
  619.       Raise (ER_FILES_TOO_LARGE)
  620.     ELSE
  621.       /* Disk filled; prompt for another. */
  622.       WriteF ('\n\s', IF filesMoved THEN '' ELSE '\nNo files moved/copied.')
  623.       WriteF ('\nUnused bytes = \d.',
  624.               Mul ((destInfo.numblocks - destInfo.numblocksused),
  625.                    destInfo.bytesperblock))
  626.  
  627.       WriteF ('\nInsert next volume.  Press RETURN to proceed, \aQ\a or \aq\a to discontinue...')
  628.       char := Inp (stdout)
  629.       IF char <> 10 THEN WHILE Inp (stdout) <> 10 DO NOP  /* Flush input buffer. */
  630.       IF upperChar (char) = "Q" THEN Raise (ER_USER_ABORT)
  631.  
  632.       UnLock (destLock)
  633.       IF (destLock := Lock (argDestPath, SHARED_LOCK)) = NIL THEN Raise (ER_DEST_LOCK)
  634.       newDisk := TRUE
  635.     ENDIF
  636.   ENDWHILE
  637.  
  638.   /* Display unused bytes on destination before leaving program. */
  639.   destInfoSuccess := Info (destLock, destInfo)
  640.   IF destInfoSuccess = FALSE THEN Raise (ER_DEST_INFO)
  641.   WriteF ('\n\nUnused bytes = \d.',
  642.           Mul ((destInfo.numblocks - destInfo.numblocksused),
  643.                destInfo.bytesperblock))
  644.  
  645.   /* Clean up. */
  646.   UnLock (destLock)
  647.  
  648.   WriteF ('\n\n')
  649.   CleanUp (0);
  650.  
  651. EXCEPT
  652.  
  653.   WriteF ('\n\n')
  654.  
  655.   SELECT exception
  656.     CASE ER_OPEN_ARPLIBRARY;
  657.       WriteF ('Error opening arp.library V39+')
  658.     CASE ER_USAGE
  659.       WriteF ('   Usage:  Fill [<options>] <source> <dest>' +
  660.               '\n     <source>  Any valid DOS "dev:dir/filepat", ARP wildcards supported' +
  661.               '\n     <dest>    Any valid DOS "dev:dir"' +
  662.               '\n     [<options>]' +
  663.               '\n      -b##  Buffer size in kbytes (1-\d; default 20)' +
  664.               '\n      -c    Copy files only, don\at delete source (default MOVE FILES)' +
  665.               '\n      -e##  Error margin, add blocks to storage estimate (1-\d; default 0)' +
  666.               '\n      -n    No DOS overhead considerations (use on MS-DOS floppies)',
  667.                MAX_ARG_BUFSIZE, MAX_ARG_ERRORMARGIN)
  668.     CASE ER_DEST_LOCK;    WriteF (' *** Destination \s does not exist.', argDestPath)
  669.     CASE ER_SOURCE_SPEC;  WriteF (' *** No entries found.')
  670.     CASE ER_MEM;          WriteF (' *** Insufficient memory.')
  671.     CASE ER_DEST_INFO;    WriteF (' *** Error occurred while getting destination Info.')
  672.     CASE ER_FILES_TOO_LARGE
  673.                           WriteF (' *** Remaining file(s) too large to fit on destination.')
  674.     CASE ER_USER_ABORT;   WriteF (' *** Program aborted by request.')
  675.     DEFAULT;              NOP
  676.   ENDSELECT
  677.  
  678.   IF destLock THEN UnLock (destLock)
  679.  
  680.   /* Arp stuff. */
  681.   IF anchorPath THEN FreeAnchorChain (anchorPath)
  682.   IF arpbase THEN CloseLibrary (arpbase)
  683.  
  684.   WriteF ('\n\n')
  685.   CleanUp (RETURN_WARN);
  686.  
  687. ENDPROC
  688.