home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / ffmb003.zip / FFMB.CMD
OS/2 REXX Batch file  |  1994-04-04  |  54KB  |  1,318 lines

  1. /* FFMB.CMD lr0.003  (C)opyright 1994, Dirk Theurer */
  2. /* -------------------------------------------------------------------------
  3.    FFMB.CMD lr0.003  (C)opyright 1994, Dirk Theurer
  4.    BinkleyTerm-style Flow Filed Mail Bundle handler
  5.  
  6.    Disclaimer:
  7.  
  8.      I've done my best to ensure that everything that FFMB does cannot cause
  9.      any problems. However, if you use it, you do so at your own risk. If
  10.      you use FFMB then you indicate your agreement to this notice. (I *HATE*
  11.      the need for these disclaimers. <sigh>)
  12.  
  13.    Note on these docs:
  14.  
  15.      Sorry if they seem a little higgledy-piggledy. Trying to find some
  16.      method of grouping information seemed to be particularly difficult for
  17.      FFMB. I hope I've covered everything though.
  18.  
  19.      I refer to specific paths and files here and there. Please change all
  20.      such references to suit your system.
  21.  
  22.    Description:
  23.  
  24.      FFMB is a REXX script that "manages" BinkleyTerm-style flow file-
  25.      referenced mail bundles according to their size. FFMB's capabilities
  26.      are commonly used to ensure that compressed mail bundles do not exceed
  27.      some maximum size. FFMB is fully zone-aware, and uses .BSY semaphores
  28.      in the same manner as Squish and BinkleyTerm to ensure no collisions
  29.      occur with any other .BSY-aware mail processors or mailers.
  30.  
  31.    Where's it useful?
  32.  
  33.      Well, I'm only familiar with the BinkleyTerm/Squish environment. For
  34.      this, FFMB does the job. If you run some other mailer/mail processor
  35.      combination, you're (mostly) on your own. (Take a look through these
  36.      docs anyway to see if it might be of use in your environment. If you
  37.      are using SizeMail now, FFMB will most likely be applicable.) Note that
  38.      FFMB concerns itself mostly with Squish (no changes are needed to
  39.      BinkleyTerm at all). If you use Squish and have it setup to process
  40.      mail as in a Bink environment, FFMB will work "as advertised."
  41.  
  42.    Version:
  43.  
  44.      FFMB is in "limited release". This can be interpreted three ways:
  45.      1) FFMB is limited (not really - it does what its supposed to);
  46.      2) It should only see limited distribution (I don't really care);
  47.      3) It's only seen testing on a limited number of systems.
  48.  
  49.      That last one is changing quickly. As soon as I hear from a few people
  50.      that have implemented FFMB successfully, I'll change the release type
  51.      to "public".
  52.  
  53.    Distribution:
  54.  
  55.      By all means send FFMB to someone you think might find a use for it.
  56.      (Heck, give it to ANYONE.) Only thing I ask is that the original
  57.      FFMB.CMD file is not modified, and that the compressed filename should
  58.      be FFMB###.ext where ### is the version number, and ext is appended by
  59.      whatever you use to squash files.
  60.  
  61.      FFMB is FREEWARE. If you find a good use for it, GREAT! (If you cannot
  62.      seem to get some "it's so great I could puke" out of your system, send
  63.      me mail telling me all about how your gerbil finally convinced you to
  64.      use FFMB... the more rice pudding involved, the better.)
  65.  
  66.    What's new:
  67.  
  68.      lr0.003 (04-04-94)
  69.        - FFMB now deletes "old" zero-length mail bundles in the mailer's
  70.          outbound when necessary.
  71.        - Both "Bundle size limiting" and "Cleanup" now invoke FFMB's "FFMB-
  72.          cleanup" mode. Basically, this means that invoking FFMB in its
  73.          "FFMB-cleanup" mode is not really necessary as it'll be implicitly
  74.          run every time FFMB is run. (You can still use FFMB's FFMB-cleanup
  75.          mode separately if you want.)
  76.        - New option to not report the paths specified on the command line if
  77.          a log is asked for. (See command line parameters for modes.)
  78.        - Log information types changed a bit (*, +, etc).
  79.        - My examples for determining "rule of thumb" settings for limiting
  80.          bundle sizes were out to lunch. Hopefully the new ones make more
  81.          sense.
  82.        - Removed note about "This version of FFMB does NOT limit the size of
  83.          un-bundled mail. I might include this in some future version". Non-
  84.          bundled mail bundles cannot be limited without mucking about with
  85.          odd filenames, and generating custom flow files... (Hmmm... If
  86.          anyone can see where limiting the size of uncompressed mail packets
  87.          might be of some use, let me know and I'll see what can be done.)
  88.        - Someone suggested that perhaps the flow files did not need to be
  89.          moved, and that perhaps SizeMail's method of using a single "TMP"
  90.          directory would simplify things. At first I thought - "Yeah, why AM
  91.          I making things so difficult?" I found out that SizeMail's method
  92.          of handling outbounds CAN and WILL cause collisions in a multi-zone
  93.          environment (same net/node in different zones). FFMB's method has
  94.          no problems with zones. An added benefit is that bundles for nodes
  95.          that are .BSY in the mailer's outbound when FFMB starts can STILL
  96.          be moved as the .BSY is in the mailer's outbound - not where FFMB
  97.          is handling files. (i.e. Moving bundles bound for a node that is
  98.          .BSY is a strict no-no.) Some of these benefits came to light
  99.          "after the fact", but are there nonetheless. <grin> About the only
  100.          down-side to all this is that FFMB must be run at least twice (once
  101.          to limit bundle sizes, and then again to move "leftover" bundles
  102.          after the mail processor is done).
  103.        - My noted worries in the last version about FFMB not handling .BSY
  104.          semaphores were apparently related more to worries about Squish's
  105.          .BSY semaphores. This is only of concern when there are multiple
  106.          copies of Squish running at the same time. As it is, FFMB will not
  107.          handle multiple instances of Squish that are tossing to the same
  108.          outbound. (*Maybe* some future version will, but only if I get some
  109.          requests for such support.)
  110.        - I re-organized the docs a bit. Hopefully they're a little more
  111.          coherent now.
  112.  
  113.      lr0.002 (03-28-94)
  114.        - First limited release.
  115.  
  116.      ir0.001
  117.        - Internal release (development).
  118.  
  119.    What does FFMB require?
  120.  
  121.      - FFMB requires a REXX interpreter. (With OS/2, "basic" REXX must be
  122.        installed. If you're using some other REXX interpreter, see its
  123.        requirements.)
  124.  
  125.      - FFMB - as its description implies - deals (mostly) with mail bundles
  126.        referenced by BinkleyTerm-style flow files. (This is not quite true,
  127.        but I doubt that you'd want other flow-file referenced files being
  128.        "sequenced" the same way as mail bundles.)
  129.  
  130.      - FFMB requires an outbound area for the mail processor SEPARATE from
  131.        the one your mailer uses. (See "Installing..." section.)
  132.  
  133.      - In this version, FFMB requires that the two outbound directories
  134.        (mailer/mail processor) are on the same drive. (This shouldn't be a
  135.        problem as you originally had your single outbound on a single drive,
  136.        eh?)
  137.  
  138.      - If you're already aware of BinkleyTerm's and Squish's requirements
  139.        for "zone-awareness", you can ignore the following note.
  140.  
  141.        To handle zones properly, FFMB requires that its outbound paths have
  142.        no extensions in the last directory name. i.e.
  143.  
  144.          C:\BINK\OUTBOUND          OK
  145.          C:\BINK\OUTBOUND.         OK (FFMB will strip the trailing period)
  146.          C:\BINK\OUTBOUND.EXT      Not OK
  147.          C:\BINK.EXT\OUTBOUND      OK
  148.          C:\BINK.EXT\OUTBOUND.EXT  Not OK
  149.  
  150.        FFMB will generate an error message and exit if an extension is
  151.        specified for the last directory name of either outbound.
  152.  
  153.    Howzzit work?
  154.  
  155.      In "Bundle size limiting" mode (see "FFMB modes" section), FFMB looks
  156.      through Squish's outbound area and sees if any flow-file referenced
  157.      files are larger than you specify. If a "over-size" file is found, the
  158.      mailer's outbound is checked for any files that have the same root
  159.      filename. FFMB finds the next sequence number for that root filename.
  160.      FFMB then renames/moves the file to the mailer's outbound with this new
  161.      sequence number. (Sequences are 0 to 9, and A to Z.) FFMB then moves
  162.      the associated flow file to one of two areas: if no .BSY semaphore
  163.      exists in your mailer's outbound for this zone:net/node, the flow file
  164.      is moved to the mailer's outbound for this zone; if a .BSY semaphore is
  165.      found, the flow file is moved to FFMB's "hold" area for the current
  166.      zone. (FFMB's "hold" areas are always a $$FFMB$$.TMP directory in your
  167.      mailer's outbound for the current zone.)
  168.  
  169.      Notes: - "Root filename" for FFMB means the "filename.dd" portion of
  170.               "filename.dd?". The "?" is the sequence number. Squish's
  171.               sequence number is ignored by FFMB, as the only place sequence
  172.               numbers are of importance is in your MAILER'S outbound.
  173.             - FFMB simply *moves* the file (with the new name in its new
  174.               directory). This means that Squish will tend to generate the
  175.               same sequence number over and over (usually 0). This should
  176.               NOT cause any problems, but FFMB does not have any problems
  177.               when files with some other sequence numbers are in Squish's
  178.               outbound. (Confused? _I_ am. <grin>)
  179.  
  180.      In "Cleanup" mode, FFMB will move ALL files in Squish's outbound area,
  181.      using all the other rules for the "Bundle size limiting" mode.
  182.  
  183.      FFMB also has a separate mode ("FFMB-cleanup") that just checks its own
  184.      holding areas for any flow files that were previously .BSY. It still
  185.      checks for matching .BSY semaphores in the mailers outbound, but will
  186.      move all "non-.BSY" files (flowed or not) to the mailer's outbound.
  187.  
  188.      Notes: - FFMB-handled flow files are always *appended* to a flow file
  189.               in either your mailer's outbound, or in FFMB's holding area if
  190.               a .BSY semaphore was encountered for that flow file.
  191.             - Files in Squish's outbound that are not referenced by flow
  192.               files are only handled in FFMB's "Cleanup" mode. FFMB will
  193.               always indicate that these are "orphan" or "non-flow" files.
  194.               (Perhaps a little misleading, but FFMB is supposed to only be
  195.               dealing with flow-file referenced mail bundles. <grin>) This
  196.               includes NON-BUNDLED MAIL (.PKT, ?UT, etc). Any files that are
  197.               not referenced by flow files are moved either to your mailer's
  198.               outbound, or to FFMB's holding area if a .BSY equivalent is
  199.               in your mailer's outbound. These are moved at the same time
  200.               and the same way as other flow files that might be in FFMB's
  201.               holding area when FFMB is run in "FFMB-cleanup" mode.
  202.  
  203.    FFMB modes:
  204.  
  205.      Notes: - FFMB can be run in any mode at any time. There are no entrance
  206.               or exit requirements except that directories must be specified
  207.               where indicated, and those directories must exist.
  208.             - Command line parameters are not case-sensitive, and trailing
  209.               back-slashes for directory specifications are optional.
  210.  
  211.      Bundle size limiting
  212.  
  213.        FFMB will move any flow files and their associated bundles that
  214.        exceed a specified size from your mail processor's outbound to where
  215.        your mailer expects to find outbound mail and files.
  216.  
  217.        This mode is commonly run from a mail processor during "breaks" (see
  218.        Squish.Cfg's MaxMsgs keyword, and Squish-Route.Cfg's DOS keyword).
  219.  
  220.        FFMB command line parameters for bundle size limiting:
  221.  
  222.          Max_bytes Originating_outbound Destination_outbound [Log_name [*]]
  223.  
  224.          - Max_bytes             *Required* All bundles that exceed this
  225.                                  limit are moved from Originating_outbound
  226.                                  to Destination_outbound.
  227.          - Originating_outbound  *Required* Where your mail processor places
  228.                                  outbound bundles.
  229.          - Destination_outbound  *Required* Where your mailer expects to
  230.                                  find outbound stuff (mail, bundles, flow
  231.                                  files, etc).
  232.          - Log_name              (Optional) Log file of FFMB activity.
  233.          - *                     (Optional but requires Log_name) Disables
  234.                                  some path reporting in the log. ("*" can be
  235.                                  ANYTHING - if it's non-blank, FFMB will not
  236.                                  log "Orig./Dest. path".)
  237.  
  238.        Example:
  239.  
  240.          FFMB 500000 C:\SQUISH\OUTBOUND C:\BINK\OUTBOUND C:\LOG\FFMB.LOG
  241.  
  242.        Note: This mode only handles FLOW FILES and the files they reference.
  243.              FFMB will NOT look for or handle ANY other files in this mode.
  244.  
  245.      Cleanup after your mail processor
  246.  
  247.        This mode is similar to the size limiting mode except that ALL files
  248.        in Originating_outbound are moved to Destination_outbound. FFMB will
  249.        move all flow files and their associated bundles (properly sequenced)
  250.        to your mailer's outbound area. FFMB checks for .BSY semaphores and
  251.        leaves flow files that match them in it's own holding area. All other
  252.        files that remain in Originating_outbound are then moved to your
  253.        mailer's outbound (also using .BSY rules). (Any files not referenced
  254.        by flow files are noted as "orphan" or "non-flowed".)
  255.  
  256.        This mode is normally run after your mail processor has exited.
  257.  
  258.        FFMB command line parameters for cleanup after your mail processor
  259.  
  260.          C[LEANUP] Originating_outbound Destination_outbound [Log_name [*]]
  261.  
  262.          - CLEANUP               *Required* Tells FFMB to move *everything*
  263.                                  from Originating_outbound to Destination_
  264.                                  outbound.
  265.          - Originating_outbound  *Required* Where your mail processor places
  266.                                  outbound bundles.
  267.          - Destination_outbound  *Required* Where your mailer expects to
  268.                                  find outbound files (mail, bundles, flow
  269.                                  files, etc).
  270.          - Log_name              (Optional) Log file of FFMB activity.
  271.          - *                     (Optional but requires Log_name) Disables
  272.                                  some path reporting in the log. ("*" can be
  273.                                  ANYTHING - if it's non-blank, FFMB will not
  274.                                  log "Orig./Dest. path".)
  275.  
  276.        Example:
  277.  
  278.          FFMB CLEANUP C:\SQUISH\OUTBOUND C:\BINK\OUTBOUND C:\LOG\FFMB.LOG
  279.  
  280.        Note: Only the first character of the CLEANUP parameter is required.
  281.  
  282.      FFMB-cleanup to move previously .BSY flow files
  283.  
  284.        Cleans up FFMB's holding area(s) if possible. FFMB will not write to
  285.        flow files in your mailer's outbound area if a .BSY semaphore exists
  286.        for a node. Running FFMB in this mode checks if those .BSY semaphores
  287.        still exist, and if not, move the flow files for such nodes. This
  288.        mode should be run often to ensure that flow files are moved from
  289.        FFMB's holding area to your Binkley-style outbound as quickly as
  290.        possible. (Suggestion: Run this mode after ALL mail processor exits,
  291.        and also before you start your mailer.)
  292.  
  293.        FFMB command line parameters to move previously .BSY flow files
  294.  
  295.          F[FMBCLEANUP] Destination_outbound [Log_name [*]]
  296.  
  297.          - FFMBCLEANUP           *Required* Tells FFMB to check its own hold
  298.                                  areas and move everything it can into
  299.                                  Destination_outbound.
  300.          - Destination_outbound  *Required* Where your mailer expects to
  301.                                  find outbound files (mail, bundles, flow
  302.                                  files, etc).
  303.          - Log_name              (Optional) Log file of FFMB activity.
  304.          - *                     (Optional but requires Log_name) Disables
  305.                                  some path reporting in the log. ("*" can be
  306.                                  ANYTHING - if it's non-blank, FFMB will not
  307.                                  log "Orig./Dest. path".)
  308.  
  309.        Example:
  310.  
  311.          FFMB FFMBCLEANUP C:\BINK\OUTBOUND C:\LOG\FFMB.LOG
  312.  
  313.        Notes: - The Originating_outbound needed for other modes is not
  314.                 needed here. (FFMB looks only for its own holding areas in
  315.                 this mode.)
  316.               - Only the first character of the FFMBCLEANUP parameter is
  317.                 required.
  318.               - FFMB will not create any holding areas when cleaning up
  319.                 after itself. It checks only for previously created areas.
  320.  
  321.    Installing FFMB for Squish:
  322.  
  323.      Notes: - The following outlines how to install FFMB for Squish ONLY! If
  324.               you are using some other mail processor, please go through
  325.               these docs carefully, and if it is not clear what's going on,
  326.               let me know.
  327.             - Mailer/mail processor environments vary WILDLY! This section
  328.               outlines the basic stuff. If you have something substantially
  329.               different, just make sure you know what FFMB actually does.
  330.  
  331.      - Copy FFMB.CMD to a directory listed in your PATH.
  332.  
  333.      - Create a new directory - WITHOUT an extension in its name - for
  334.        Squish's outbound. (You can delete Squish's old *.SQ directory
  335.        parallel to your mailer's outbound. Squish will re-create it parallel
  336.        to its new outbound.)
  337.  
  338.      - Change Squish's configuration Outbound keyword to match this new
  339.        directory. For example, if your original Squish and Bink were
  340.        configured to place/look for mail in C:\BINK\OUTBOUND, create a
  341.        new directory called C:\SQUISH\OUTBOUND, and change the OutBound
  342.        config keyword in Squish.Cfg to C:\SQUISH\OUTBOUND. No changes to
  343.        Bink's config.
  344.  
  345.      - If you do not already have a MaxMsgs entry in your Squish.Cfg, add a
  346.        line similar to:
  347.  
  348.          MaxMsgs 500
  349.  
  350.        (Check the Squish docs for what this does.) I've noticed that in
  351.        Fidonet, a "MaxMsgs 500" tends to produce compressed bundles of about
  352.        400K for nodes receiving all message areas. A workable rule of thumb
  353.        is to take the maximum size (in K) you want your bundles to be, add
  354.        10% and set MaxMsgs to this number. To move bundles of about this
  355.        size get moved by FFMB, subtract about 35% from this MaxMsgs setting,
  356.        multiply by 1000, and use the result for FFMB's Max_bytes parameter.
  357.        For example, if you wanted to limit bundles to average ~500K, set
  358.        MaxMsgs to 550, and use 350000 for Max_bytes with FFMB. This'll get
  359.        you ARC bundles of 350K to 700K. To make FFMB's Max_bytes parameter
  360.        more closely match the bundle sizes you want, decrease the MaxMsgs
  361.        settings (FFMB will get a chance to check bundle sizes oftener, but
  362.        you'll have smaller, and more PKTs in the bundles). Of course your
  363.        mileage will vary WILDLY. Play with it.
  364.  
  365.      - In your Route.Cfg, append a line at the very end to read similar to:
  366.  
  367.          DOS FFMB 350000 C:\SQUISH\OUTBOUND C:\BINK\OUTBOUND C:\LOG\FFMB.LOG
  368.  
  369.        where: 350000              maximum bundle size in bytes
  370.               C:\BINK\OUTBOUND    your mailer's outbound
  371.               C:\SQUISH\OUTBOUND  Squish's OutBound area
  372.               C:\LOG              where you keep your logs (optional)
  373.  
  374.        Notes: - FFMB cannot control how large bundles actually get. Squish
  375.                 must exit and execute FFMB before FFMB can do any bundle
  376.                 size limiting. The key here is Squish.Cfg's MaxMsgs value.
  377.               - The "DOS FFMB..." line can be placed anywhere in Route.Cfg,
  378.                 but Squish has its own rules for when it might be used. If
  379.                 it's placed at the very end of Route.Cfg, Squish will always
  380.                 bundle up ALL mail to be bundled, and then look at the "DOS"
  381.                 line.
  382.  
  383.      - In your mail processing batch or CMD file, add a reference to FFMB so
  384.        that FFMB can cleanup after each Squish run. (This ensures that all
  385.        mail bundles from Squish are moved to your mailer's outbound.) REXX
  386.        example:
  387.  
  388.          Squishp in out squash -o
  389.          "CALL FFMB C C:\SQUISH\OUTBOUND C:\BINK\OUTBOUND C:\LOG\FFMB.LOG"
  390.          Squishp squash
  391.          "CALL FFMB C C:\SQUISH\OUTBOUND C:\BINK\OUTBOUND C:\LOG\FFMB.LOG"
  392.          signal BINKLEY
  393.  
  394.        Batch file example:
  395.  
  396.          Squishp in out squash -o
  397.          CALL FFMB C C:\BINK\OUTBOUND C:\SQUISH\OUTBOUND C:\LOG\FFMB.LOG
  398.          Squishp squash
  399.          CALL FFMB C C:\BINK\OUTBOUND C:\SQUISH\OUTBOUND C:\LOG\FFMB.LOG
  400.          goto BINKLEY
  401.  
  402.      - If you want to ensure that .BSY flow files are moved regularly, add
  403.        another line immediately before your mailer starts up. REXX example:
  404.  
  405.          "CALL FFMB F C:\BINK\OUTBOUND C:\LOG\FFMB.LOG"
  406.          btp
  407.  
  408.        Batch file example:
  409.  
  410.          CALL FFMB F C:\BINK\OUTBOUND C:\LOG\FFMB.LOG
  411.          btp
  412.  
  413.      That's about it!
  414.  
  415.    "Left-over" notes:
  416.  
  417.      - FFMB creates and uses its own "flow file holding area(s)". These are
  418.        ALWAYS created as a subdirectory off of Destination_outbound. These
  419.        directories can be removed if they are empty *AND* FFMB is not
  420.        running. (FFMB will re-create any that are needed on its next run.)
  421.  
  422.      - If FFMB encounters an outbound zone for Squish that does not already
  423.        exist for your mailer, it will be created also.
  424.  
  425.      - "Stray bundles" in your outbound have NOT actually strayed at all!
  426.        Check FFMB's holding area ($$FFMB$$.TMP in your mailer's outbound)
  427.        for the flow files that reference them. (FFMB writes ALL "oversize"
  428.        or "cleanup" files referenced by flow files to the outbound area
  429.        without checking for .BSY semaphores. HOWEVER, FFMB will *NOT* mess
  430.        with any flow files in your mailer outbound area if a .BSY semaphore
  431.        is set for a node. All flow information is written to FFMB's area
  432.        instead.)
  433.  
  434.      - This version of FFMB will NOT handle multiple instances of Squish
  435.        running at the same time! (i.e. Squish is processing mail while FFMB
  436.        is running.) I don't know if/when such support will be added to FFMB.
  437.        Note that if Squish is processing mail in a DIFFERENT "originating"
  438.        outbound than FFMB is currently processing, there should be no
  439.        problem. Just use a different FFMB log names for possible multiple
  440.        instances of FFMB.
  441.  
  442.      - Why FFMB? Well, I'm getting REAL tired of running DOS software under
  443.        OS/2. SizeMail is the only utility that provides this functionality
  444.        (that I know of), but it is "yet another crippling DOS utility".
  445.  
  446.        I should add here that FFMB is *not* a SizeMail clone or "look-
  447.        alike". It's functionality is quite similar, but FFMB and SizeMail
  448.        work within substantially different paradigms. (I didn't really look
  449.        at the what and how of SizeMail until I was just about finished with
  450.        the first release of FFMB.)
  451.  
  452.      - Why REXX? Four reasons:
  453.        1) Because I wanted to.
  454.        2) REXX interpreters are available for a variety of platforms.
  455.        3) I VERY MUCH like my software to be as configurable/customizable as
  456.           absolutely possible. I can think of nothing more flexible that a
  457.           reasonably preforming interpreted language in which the utility
  458.           can be distributed in source form. This gives the user the
  459.           ULTIMATE in flexibility! <grin>
  460.        4) Because I wanted to. (Did I just say that twice? Musta been cuz I
  461.           wanted to.)
  462.  
  463.      - I'm hoping that the next version of Squish will obviate any need for
  464.        FFMB (or equivalent). I feel that Squish should be able to manage
  465.        bundles sizes without any "outside" help.
  466.  
  467.    Support:
  468.  
  469.      FFMB and its REXX format present problems with support. Any Tom, Dick,
  470.      and/or Hairy can dink with the CMD itself, and pass it on. This being
  471.      the case, I can properly support FFMB only if you send me the COMPLETE,
  472.      ORIGINAL copy you are using. If you don't send me a copy, and I can't
  473.      reproduce the problem, you're on your own. (Maybe FReq the most recent
  474.      version from me instead - even if it appears to be the same version, it
  475.      could very well be different, depending on who's messed with it before
  476.      you got it.)
  477.  
  478.      Along with a copy of the FFMB.CMD you have, send along any pertinent
  479.      information (directory specs used, list of files for each, FFMB log
  480.      samples, command line parameters used, any error messages you got on
  481.      the screen that don't occur in FFMB's log, etc).
  482.  
  483.      If you have a mail processor that you think FFMB should work with,
  484.      please send me the name and latest version number of the processor.
  485.  
  486.      Note: Do NOT send me a copy of your mail processor! Name and version
  487.            only, please. (I've probably already got it. <grin> If I don't,
  488.            I'll try to find it locally.)
  489.  
  490.    Contact info:
  491.  
  492.      Comments, suggestions, bug reports, etc about FFMB are welcomed! I can
  493.      be reached as: - Netmail to Dirk Theurer 1:153/828@fidonet
  494.                     - e-mail to dirk.theurer@iflex.wimsey.com
  495.  
  496.      Latest version of FFMB can be FReq'd from above Fidonet address as
  497.      "FFMB" (without the quotes). If you want it sent via Internet e-mail,
  498.      request such from your e-mail address, and I'll send a UUENCODEd copy
  499.      of the (PK)ZIP v2.x format distribution copy as (however many) less-
  500.      than-8K messages. (Requests to "gate" e-mail copies to Fidonet via
  501.      Internet will be ignored!)
  502.  
  503.    A good suggestion:
  504.  
  505.      Move all these leading comments (except the first "REXX-identifying"
  506.      line) to some other text file. FFMB should load a little faster then.
  507.      (Keep the original package you got in case you need support though. I
  508.      really DO require that I get a "matchable" copy to support FFMB
  509.      properly.)
  510.  
  511.    Enjoy!
  512.  
  513.    ------------------------------------------------------------------------- */
  514.  
  515. /* -------------------------------------------------------------------------
  516.    Note to (wanna-be) REXX tinkerers:
  517.  
  518.    FFMB.CMD lr0.003 is not a trivial REXX script. Dink with it at your own
  519.    risk.
  520.  
  521.    I mentioned in the last version that perhaps this script would be re-
  522.    organized. I've gone over it and it doesn't seem to need it as badly as I
  523.    thought.
  524.  
  525.    Changes from previous versions are NOT noted anywhere! ANYTHING can be,
  526.    and is changed whenever I feel like (usually to make things easier to
  527.    read, conserve memory, increase performance, and sometimes just for the
  528.    heck of it).
  529.  
  530.    I am quite aware that some things could be done much easier by invoking
  531.    REXXUTIL, but I'll sacrifice script readability and portability over
  532.    memory any day.
  533.    ------------------------------------------------------------------------- */
  534.  
  535.  "@echo off"
  536.  arg ffmbmode origdir destdir logname logpaths
  537.  
  538.  /* reset elapsed time timer */
  539.  starttime=time(R)
  540.  
  541.  call initffmb
  542.  
  543.  /* debug is used to display various info while script is developed. Set */
  544.  /* this to 0 if not needed (do NOT just remove this statement).     */
  545.  /* Note: Limited release versions of FFMB have debug set to 0. Set to 1 */
  546.  /*      if you want additional information displayed on-screen.     */
  547.  debug=0
  548.  
  549.  /* first parameter exists? */
  550.  if ffmbmode="" then
  551.    call PRELOGFFMBERROR 1 "No mode specified."
  552.  
  553.  /* determine mode */
  554.  sizelimit=0
  555.  select
  556.    when left(ffmbmode,1)="C" then /* Cleanup */
  557.      ffmbmode="C"
  558.    when left(ffmbmode,1)="F" then /* FFMB-cleanup */
  559.      ffmbmode="F"
  560.    otherwise do           /* Bundle size limiting */
  561.      if datatype(ffmbmode)<>"NUM" then
  562.        call PRELOGFFMBERROR 1 "Invalid byte limit specified."
  563.      else
  564.        if ffmbmode<0 then
  565.      call PRELOGFFMBERROR 1 "Negative(?!?) byte limit specified."
  566.      sizelimit=ffmbmode
  567.      ffmbmode="S"
  568.    end
  569.  end
  570.  
  571.  if ffmbmode="F" then do
  572.    /* shuffle command line parameters (no origdir for FFMB-cleanup) */
  573.    logpaths=logname
  574.    logname=destdir
  575.    destdir=origdir
  576.    origdir=""
  577.  end
  578.  else do
  579.    /* all other modes require origdir */
  580.    origdir=verifydir(origdir,"Originating outbound")
  581.    /* get pattern for originating directory name */
  582.    origpattern=left(origdir,length(origdir)-1)
  583.  end
  584.  
  585.  /* destdir is required for all modes */
  586.  destdir=verifydir(destdir,"Destination outbound")
  587.  /* get pattern for destination directory name */
  588.  destpattern=left(destdir,length(destdir)-1)
  589.  
  590.  /* verify/setup optional log stuff */
  591.  if logname="" then
  592.    say "* "logdatetime()" "cmdname" (No log)"
  593.  else
  594.    if \findfile(logname) then do
  595.      if \openappend(logname) then
  596.        call PRELOGFFMBERROR 1 logname" cannot be opened for writing."
  597.      ot=lineout(logname,"")
  598.      ot=lineout(logname,"  "cmdname" "cmdvers" "cmdcopy)
  599.      ot=lineout(logname,"  "cmddesc)
  600.      ot=lineout(logname,"  "left("",76,"─"))
  601.      ot=lineout(logname,"")
  602.      say "  "logdatetime()" "cmdname" Log file: "logname" (created)"
  603.    end
  604.    else do
  605.      if \openappend(logname) then
  606.        call PRELOGFFMBERROR 1 logname" cannot be opened for writing."
  607.      say "  "logdatetime()" "cmdname" Log file: "logname
  608.    end
  609.  
  610.  /* verify that origdir and destdir exist */
  611.  /* save current dir (dir tests are done by trying to change to those */
  612.  /* directories)                              */
  613.  savedir=directory()
  614.  if ffmbmode<>"F" then
  615.    if \changedir(origdir) then
  616.      call FFMBERROR 1 origdir" does not exist."
  617.  if \changedir(destdir) then
  618.    call FFMBERROR 1 destdir" does not exist."
  619.  
  620.  /* used for all FFMB-specific and temporary stuff */
  621.  ffmbfile="$$FFMB$$"
  622.  
  623.  /* restore current directory */
  624.  ot=directory(savedir)
  625.  
  626.  select
  627.    when ffmbmode="F" then do
  628.      call writelog logname,"*","FFMB-cleanup"
  629.      signal FFMBCLEANUP
  630.    end
  631.    when ffmbmode="C" then do
  632.      call writelog logname,"*","Cleanup"
  633.      signal FFMBMOVEBUNDLES
  634.    end
  635.    otherwise do
  636.      call writelog logname,"*","Move bundles > "sizelimit" bytes"
  637.      signal FFMBMOVEBUNDLES
  638.    end
  639.  end
  640.  
  641.  /* That's it! Everything else is signalled or called. */
  642.  
  643.  FFMBMOVEBUNDLES:
  644.  /* ------------------------------------------------------------------------
  645.     Non-cleanup mode (move oversize files only) for all detectable zones
  646.  
  647.     - get zone list (directory list of origdir.*)
  648.     - move bundles in base zone
  649.     - loop through remaining zones
  650.       - move bundles in current zone
  651.     - run FFMBCLEANUP mode
  652.     ------------------------------------------------------------------------ */
  653.  
  654.  if logpaths="" then do
  655.    call writelog logname,":","Orig. path: "origdir
  656.    call writelog logname,":","Dest. path: "destdir
  657.  end
  658.  
  659.  /* ffmbdir is FFMB's "holding" area for flowfiles that have .BSY semaphores */
  660.  ffmbdir=destdir""ffmbfile".TMP\"
  661.  /* if FFMB's holding area doesn't exist, try to create it */
  662.  if \changedir(ffmbdir) then
  663.    if \makedir(ffmbdir) then
  664.      call FFMBERROR 1 ffmbdir" could not be created."
  665.  
  666.  if findfile(origdir"*.*") then do
  667.    if ffmbmode="C" | findfile(origdir"*.?LO") then do
  668.      call writelog logname," ","Processing default zone:"
  669.      call FFMBMOVEZONE
  670.      call writelog logname,"*","Moved "movedflowed" flowed file(s) for default zone."
  671.      if ffmbmode="C" & movedunflowed<>0 then
  672.        call writelog logname,"*","Moved "movedunflowed" non-flowed file(s) for default zone."
  673.    end
  674.  end
  675.  
  676.  /* get zone directory list */
  677.  call getzonelist origpattern,origdir""ffmbfile".$$$"
  678.  if debug then
  679.    do i=1 to zonelist.0; say "DEBUG Zone directory "right("  "i,3)": "zonelist.i; end
  680.  
  681.  do zone=1 to zonelist.0
  682.    origdir=zonelist.zone
  683.    /* extract hex-zone */
  684.    zonei=substr(origdir,lastpos(".",origdir)+1)
  685.    /* add backslash to new origdir */
  686.    origdir=origdir"\"
  687.    if findfile(origdir"*.*") then do
  688.      if ffmbmode="C" | findfile(origdir"*.?LO") then do
  689.        call writelog logname," ","Processing zone "x2d(zonei)" ("zonei"h):"
  690.  
  691.        /* setup new destdir */
  692.        destdir=destpattern"."zonei"\"
  693.        /* if destdir for this zone does not exist, create it */
  694.        if \changedir(destdir) then
  695.      if \makedir(destdir) then
  696.        call FFMBERROR 1 destdir" (zone "x2d(zonei)") could not be created."
  697.  
  698.        /* setup FFMB's "holding" area for this zone */
  699.        ffmbdir=destdir""ffmbfile".TMP\"
  700.        /* if FFMB's holding area doesn't exist, try to create it */
  701.        if \changedir(ffmbdir) then
  702.      if \makedir(ffmbdir) then
  703.        call FFMBERROR 1 ffmbdir" (zone "x2d(zonei)") could not be created."
  704.  
  705.        /* return to default directory after dir checks */
  706.        ot=directory(savedir)
  707.  
  708.        call FFMBMOVEZONE
  709.        call writelog logname,"*","Moved "movedflowed" flowed file(s) for zone "x2d(zonei)"."
  710.        if ffmbmode="C" & movedunflowed<>0 then
  711.      call writelog logname,"*","Moved "movedunflowed" non-flowed file(s) for zone "x2d(zonei)"."
  712.  
  713.      end /* flow files exist for zone, or in cleanup mode */
  714.    end /* files exist for zone */
  715.  end /* zone list loop */
  716.  
  717.  /* setup for FFMB-cleanup mode */
  718.  origdir=origpattern"\"
  719.  destdir=destpattern"\"
  720.  call writelog logname,"*","Implied FFMB-cleanup"
  721.  signal FFMBCLEANUP
  722.  
  723.  FFMBCLEANUP:
  724.  /* ------------------------------------------------------------------------
  725.     Move non-.BSY flow files for all detectable zones from FFMB's holding
  726.     area(s).
  727.  
  728.     - move non-.BSY flow files to outbound (destdir) for default zone
  729.     - get zone list (directory list of destdir.*)
  730.     - loop through remaining zones
  731.       - move non-.BSY flow files to outbound (destdir) for current zone
  732.     - exit
  733.     ------------------------------------------------------------------------ */
  734.  
  735.  if ffmbmode="F" & logpaths="" then
  736.    call writelog logname,":","Path: "destdir
  737.  
  738.  /* define ffmb's holding area */
  739.  ffmbdir=destdir""ffmbfile".TMP\"
  740.  
  741.  if findfile(ffmbdir"*.*") then do
  742.    call writelog logname," ","Processing default zone:"
  743.    call FFMBCLEANUPZONE
  744.    call writelog logname,"*","""Unhold"" "movedflow" flow / "movednonflow" non-flow file(s) for default zone."
  745.  end
  746.  
  747.  /* get zone directory list */
  748.  call getzonelist destpattern,destdir""ffmbfile".$$$"
  749.  if debug then
  750.    do i=1 to zonelist.0; say "DEBUG Zone directory "right("  "i,3)": "zonelist.i; end
  751.  
  752.  do zone=1 to zonelist.0
  753.    destdir=zonelist.zone
  754.    /* extract hex-zone */
  755.    zonei=substr(destdir,lastpos(".",destdir)+1)
  756.    /* add backslash to new destdir */
  757.    destdir=destdir"\"
  758.  
  759.    /* define FFMB's "holding" area for this zone */
  760.    ffmbdir=destdir""ffmbfile".TMP\"
  761.  
  762.    /* process zone if FFMB's holding area exists and something is in it */
  763.    if findfile(ffmbdir"*.*") then do
  764.      call writelog logname," ","Processing zone "x2d(zonei)" ("zonei"h):"
  765.      call FFMBCLEANUPZONE
  766.      call writelog logname,"*","""Unhold"" "movedflow" flow / "movednonflow" non-flow file(s) for zone "x2d(zonei)"."
  767.    end
  768.  end /* zone list loop */
  769.  signal ENDFFMB
  770.  
  771.  
  772.  FFMBMOVEZONE:
  773.  
  774.  /* ------------------------------------------------------------------------
  775.     Process:
  776.  
  777.     - Get list of ?lo files from origdir
  778.     - Go through ?lo file list
  779.       - current flow file
  780.     - if bundle larger than sizelimit:
  781.       - re-sequence and move bundle to destdir
  782.       - write flow entry to ffmb flow file
  783.     - else
  784.       - write flow entry to temp flow file
  785.       - when flow file finished:
  786.     - if any bundles not moved:
  787.       - ren temp flow file to orig flow filename
  788.     - else
  789.       - del temp flow file
  790.       - if ffmb flow file movable to destdir, move (append) it
  791.  
  792.     ------------------------------------------------------------------------ */
  793.  
  794.  /* get list of ?LO files in origdir */
  795.  call getfilelist origdir"*.?LO",ffmbdir""ffmbfile".$$$"
  796.  if debug then
  797.    do i=1 to filelist.0; say "DEBUG Flow file "right("  "i,3)": "filelist.i; end
  798.  
  799.  /* initialize number of files moved */
  800.  movedflowed=0
  801.  movedunflowed=0
  802.  
  803.  /* temporary flow file */
  804.  tempflow=origdir""ffmbfile".FLW"
  805.  
  806.  /* go through all flow files and verify/move/whatever bundles */
  807.  do i=1 to filelist.0
  808.    /* delete temp flow file if it exists */
  809.    if findfile(tempflow) then do
  810.      call closefile tempflow
  811.      "del "tempflow
  812.    end
  813.    ffmbflow=ffmbdir""filespec("N",filelist.i)
  814.    if debug then do
  815.      say "DEBUG Originating flowfile: "filelist.i
  816.      say "DEBUG Destination flowfile: "ffmbflow
  817.      say "DEBUG   Temporary flowfile: "tempflow
  818.    end
  819.    /* loop through list of bundles in flow file */
  820.    holdbundles=0 /* count how many are NOT moved to outbound */
  821.    call openread filelist.i
  822.    do while lines(filelist.i)
  823.      origname=translate(linein(filelist.i))
  824.      if debug then say "DEBUG          Bundle line: "origname
  825.      /* determine bundle type (delete/truncate/keep) */
  826.      if left(origname,1)="^" then do
  827.        origtype="^" /* delete (don't think this is ever used) */
  828.        origname=substr(origname,2)
  829.      end
  830.      else
  831.        if left(origname,1)="#" then do
  832.      origtype="#" /* truncate (the norm?) */
  833.      origname=substr(origname,2)
  834.        end
  835.        else
  836.      origtype="" /* keep (don't think this is ever used) */
  837.      /* end of bundle type */
  838.      if \findfile(origname) then
  839.        call writelog logname,"!"," - NO FILE: "origname" (orphan flow entry)"
  840.      else do
  841.        /* if in cleanup mode, or if bundle is larger that "sizelimit" */
  842.        if ffmbmode="C" | stream(origname,"C","Query size")>sizelimit then do
  843.      /* got a bundle to move */
  844.      call writelog logname," ","Orig. file: "origname" ("stream(origname,"C","Query size")")"
  845.      /* bundle name minus sequence number */
  846.      testname=filespec("N",origname)
  847.      testname=left(testname,pos(".",testname)+2)
  848.      if findfile(destdir"*."right(testname,2)"*") then do
  849.        /* see if old bundles are zero length and old */
  850.        tempfile=origdir""ffmbfile".$$$"
  851.        "dir /f /a-d /od "destdir"*."right(testname,2)"* > "tempfile
  852.        call openread tempfile
  853.        do while lines(tempfile)
  854.          readline=linein(tempfile)
  855.          if stream(tempfile,"C","Query size")<>0 then do
  856.            if oldfile(readline)>4 then
  857.          "del "readline
  858.            else /* we can quit on first file encountered that is not old */
  859.          leave
  860.          end
  861.        end
  862.        call closefile tempfile
  863.        "del "tempfile
  864.      end
  865.      /* find unique sequence number for bundle in destdir */
  866.      do seq=0 to 9
  867.        destname=destdir""testname""seq
  868.        if movebundle(origname,destname) then
  869.          leave seq
  870.      end
  871.      if seq>9 then do
  872.        do seq2=65 to 90 /* try "A" to "Z" */
  873.          destname=destdir""testname""d2c(seq2)
  874.          if movebundle(origname,destname) then
  875.            leave seq2
  876.        end
  877.      end
  878.      if debug then do
  879.        say "DEBUG      Old bundle name: "origname
  880.        say "DEBUG       Old flow entry: "origtype""origname
  881.        say "DEBUG      New bundle name: "destname
  882.        say "DEBUG       New flow entry: "origtype""destname
  883.      end
  884.  
  885.      /* finally get to actually MOVE something (yeow) */
  886.      call writelog logname,"+","  Moved to: "destname
  887.      ot=lineout(ffmbflow,origtype""destname)
  888.      movedflowed=movedflowed+1
  889.        end /* bundle was over size limit */
  890.        else do /* bundle is not over size limit */
  891.      holdbundles=1
  892.      ot=lineout(tempflow,origtype""origname)
  893.        end
  894.      end
  895.    end /* end of bundles in flow file */
  896.    call closefile filelist.i
  897.    call closefile tempflow
  898.    call closefile ffmbflow
  899.    "del "filelist.i
  900.    if findfile(tempflow) then
  901.      if holdbundles then
  902.        "ren "tempflow" "filespec("N",filelist.i)
  903.      else
  904.        "del "tempflow
  905.  
  906.    /* if new flow file exists, try to move it to outbound area */
  907.    if findfile(ffmbflow) then
  908.      call moveflowfile ffmbflow
  909.  
  910.  end /* end of current flow file */
  911.  
  912.  /* if in cleanup mode and files remain in origdir, move 'em */
  913.  if ffmbmode="C" & findfile(origdir"*.*") then do
  914.    /* get files listing */
  915.    /* NOTE! The temporary file to get this listing must !_*NOT*_! use the  */
  916.    /*        same directory as that for the files being listed! We know the */
  917.    /*        ffmbdir exists, so use it instead.                   */
  918.    call getfilelist origdir"*.*",ffmbdir""ffmbfile".$$$"
  919.    do i=1 to filelist.0
  920.      /* construct destdir .bsy name */
  921.      destbusy=filespec("N",filelist.i)
  922.      destbusy=destdir""left(destbusy,pos(".",destbusy))"BSY"
  923.      /* if destdir .bsy exists, destname is ffmbdir{}, else destdir{} */
  924.      if findfile(destbusy) then
  925.        destname=ffmbdir""filespec("N",filelist.i)
  926.      else
  927.        destname=destdir""filespec("N",filelist.i)
  928.      if debug then do
  929.        say "DEBUG Originating filename: "filelist.i
  930.        say "DEBUG Destination filename: "destname
  931.      end
  932.      if \findfile(destname) then do
  933.        call movefile filelist.i,destname
  934.        call writelog logname, "+"," Orphan to: "destname
  935.        movedunflowed=movedunflowed+1
  936.      end
  937.      else
  938.        call writelog logname, "-"," Dupe file: "filelist.i
  939.    end
  940.  end
  941.  return
  942.  
  943.  FFMBCLEANUPZONE:
  944.  /* ------------------------------------------------------------------------
  945.     Process:
  946.  
  947.     - get list of files in ffmbdir and go through them
  948.     - construct destdir .bsy filename
  949.     - if file is ?LO, then
  950.       - try to move flow file (see MOVEFLOWFILE)
  951.     - else
  952.       - if outbound area does not have a matching .bsy file, then
  953.     - create/hold .bsy file
  954.     - move file to destdir
  955.     - close/delete .bsy file
  956.  
  957.     ------------------------------------------------------------------------ */
  958.  
  959.  /* get list of files in ffmbdir */
  960.  /* NOTE! The temp file spec for retrieving this file list must !_*NOT*_!    */
  961.  /*      use the same directory as for the files being listed! We know that */
  962.  /*      destdir exists, so use it instead.                     */
  963.  call getfilelist ffmbdir"*.*",destdir""ffmbfile".$$$"
  964.  if debug then
  965.    do i=1 to filelist.0; say "DEBUG      File "right("  "i,3)": "filelist.i; end
  966.  movedflow=0
  967.  movednonflow=0
  968.  do i=1 to filelist.0
  969.    if right(filelist.i,2)="LO" then
  970.      movedflow=movedflow+moveflowfile(filelist.i)
  971.    else do
  972.      /* construct .bsy filename */
  973.      destbusy=filespec("N",filelist.i)
  974.      destbusy=destdir""left(destbusy,pos(".",destbusy))"BSY"
  975.      if debug then do
  976.        say "DEBUG Dest. flow busy file: "destbusy
  977.        say "DEBUG Originating filename: "filelist.i
  978.        say "DEBUG Destination filename: "destdir""filespec("N",filelist.i)
  979.      end
  980.      if \findfile(destbusy) then do
  981.        ot=lineout(destbusy,cmdname" "cmdvers" is holding this file open")
  982.        destname=destdir""filespec("N",filelist.i)
  983.        if \findfile(destname) then do
  984.      call movefile filelist.i,destname
  985.      call writelog logname,"+","Dest. file: "destname
  986.      call closefile destbusy
  987.      "del "destbusy
  988.      movednonflow=movednonflow+1
  989.        end
  990.        else
  991.      call writelog logname, "-"," Dupe file: "filelist.i
  992.      end
  993.      else
  994.        call writelog logname,"+"," Hold file: "filelist.i
  995.    end
  996.  end
  997.  return
  998.  
  999.  ENDFFMB:
  1000.  /* ------------------------------------------------------------------------
  1001.     Clean things up
  1002.     ------------------------------------------------------------------------ */
  1003.  
  1004.   /* switch back to original directory */
  1005.   test=directory(savedir)
  1006.  
  1007.   /* figure out elapsed time */
  1008.   seconds=trunc(time(E))
  1009.   minutes=trunc(seconds/60)
  1010.   seconds=seconds-(minutes*60)
  1011.   if seconds<10 then seconds="0"seconds
  1012.  
  1013.   /* cleanup log */
  1014.   call writelog logname,"*","Elapsed: "minutes" min "seconds" sec"
  1015.   call writelog logname,""
  1016.  
  1017.   exit 0
  1018.  
  1019.  /* ------------------------------------------------------------------------
  1020.     Error routines
  1021.     ------------------------------------------------------------------------ */
  1022.  
  1023.  PRELOGFFMBERROR:
  1024.   parse arg rlevel errormsg
  1025.   say "! "logdatetime()" "cmdname" ERROR: "errormsg
  1026.   say ""
  1027.   call honk
  1028.   exit rlevel
  1029.  
  1030.  FFMBERROR:
  1031.   parse arg rlevel errormsg
  1032.   call writelog logname,"!","ERROR: "errormsg
  1033.   call writelog logname,""
  1034.   call honk
  1035.   exit rlevel
  1036.  
  1037.  /* ------------------------------------------------------------------------
  1038.     "CALLed" routines and functions specific to FFMB
  1039.     ------------------------------------------------------------------------ */
  1040.  
  1041.  INITFFMB:
  1042.   cmdname="FFMB"
  1043.   cmdvers="lr0.003"
  1044.   cmdcopy="(C)opyright 1994, Dirk Theurer"
  1045.   cmddesc="BinkleyTerm-style Flow-Filed Mail Bundle handler"
  1046.   say ""
  1047.   say "  "cmdname" "cmdvers" "cmdcopy
  1048.   say "  "cmddesc
  1049.   say "  "left("",76,"─")
  1050.   return
  1051.  
  1052.  HONK:
  1053.   do 3; call beep 2300,100; call beep 20000,25; end
  1054.   return
  1055.  
  1056.  GETFILELIST:
  1057.   arg filelistspec,tempfilelist
  1058.   /* WARNING: DIR's /a-d switch ensures that only FILES are listed. */
  1059.   if findfile(filelistspec) then do
  1060.     "dir /f /a-d /on "filelistspec" > "tempfilelist
  1061.     call openread tempfilelist
  1062.     fn=0
  1063.     do while lines(tempfilelist)
  1064.       fn=fn+1
  1065.       filelist.fn=translate(linein(tempfilelist))
  1066.     end
  1067.     filelist.0=fn
  1068.     call closefile tempfilelist
  1069.     "del "tempfilelist
  1070.     return
  1071.   end
  1072.   fn=0
  1073.   filelist.0=fn
  1074.   return
  1075.  
  1076.  GETZONELIST:
  1077.   arg zonelistspec,tempzonelist
  1078.   /* WARNING: DIR's /ad switch ensures that only directories are listed. */
  1079.   "dir /f /ad /on "zonelistspec".* > "tempzonelist
  1080.   call openread tempzonelist
  1081.   fn=0
  1082.   do while lines(tempzonelist)
  1083.     readline=translate(linein(tempzonelist))
  1084.     temppos=lastpos(".",readline)+1
  1085.     if temppos>1 then do
  1086.       readlineext=substr(readline,temppos)
  1087.       /* is extracted extension is a "legit" zone spec length? */
  1088.       if length(readlineext)<4 then do
  1089.     /* if extension is a hex value, then accept the dir spec */
  1090.     if datatype(readlineext,"X") then do
  1091.       fn=fn+1
  1092.       zonelist.fn=readline
  1093.     end
  1094.       end
  1095.     end
  1096.   end
  1097.   zonelist.0=fn
  1098.   call closefile tempzonelist
  1099.   "del "tempzonelist
  1100.   return
  1101.  
  1102.  MOVEFLOWFILE:
  1103.   /* -----------------------------------------------------------------------
  1104.      Process:
  1105.  
  1106.     - construct destdir .bsy filename
  1107.     - if destdir does not have .bsy file, then
  1108.       - create/hold .bsy file
  1109.       - move (append) flow file to destdir
  1110.       - close/delete .bsy file
  1111.  
  1112.     ------------------------------------------------------------------------ */
  1113.   arg mfffilename
  1114.   flowbusy=filespec("N",mfffilename)
  1115.   flowbusy=destdir""left(flowbusy,pos(".",flowbusy))"BSY"
  1116.   destflow=destdir""filespec("N",mfffilename)
  1117.   if debug then do
  1118.     say "DEBUG Dest. flow busy name: "flowbusy
  1119.     say "DEBUG      Dest. flow name: "destflow
  1120.   end
  1121.   /* move (append) to destdir if .BSY file not found */
  1122.   if \findfile(flowbusy) then do
  1123.     ot=lineout(flowbusy,cmdname" "cmdvers" is holding this file open")
  1124.     call writelog logname,"+","Dest. flow: "destflow
  1125.     call openread mfffilename
  1126.     do while lines(mfffilename)
  1127.       flowline=linein(mfffilename)
  1128.       ot=lineout(destflow,flowline)
  1129.     end
  1130.     call closefile destflow
  1131.     call closefile mfffilename
  1132.     "del "mfffilename
  1133.     call closefile flowbusy
  1134.     "del "flowbusy
  1135.     return 1
  1136.   end
  1137.   else do
  1138.     call writelog logname,"+"," Hold flow: "mfffilename
  1139.     return 0
  1140.   end
  1141.  
  1142.  MOVEBUNDLE:
  1143.   /* Moves bundle to destination if it doesn't exist (removes leading drive */
  1144.   /* in destination bundle file spec)                        */
  1145.   /* NOTE: This routine is (currently) limited to moving files around on the */
  1146.   /*       SAME drive!                                 */
  1147.   arg origmovebundle, destmovebundle
  1148.   destmovebundle=filespec("P",destmovebundle)""filespec("N",destmovebundle)
  1149.   if findfile(destmovebundle) then
  1150.     return 0
  1151.   else do
  1152.     "move "origmovebundle" "destmovebundle" > nul"
  1153.     return 1
  1154.   end
  1155.  
  1156.  VERIFYDIR:
  1157.   parse arg verifydirspec, errormessage
  1158.   if verifydirspec="" then do
  1159.     say "! "logdatetime()" "cmdname" ERROR: "errormessage" not specified."
  1160.     call honk
  1161.     exit 1
  1162.   end
  1163.   /* ensure that dir spec does NOT include a period in the last dir spec */
  1164.   if right(verifydirspec,1)="\" then
  1165.     verifydirspec=left(verifydirspec,length(verifydirspec)-1)
  1166.   /* I cheat a little on this one by using filespec()'s "Name" default of */
  1167.   /* accepting ANYthing at the end as a filename if it does not have a      */
  1168.   /* trailing back-slash.                          */
  1169.   tempdirspec=filespec("N",verifydirspec)
  1170.   /* if the period is trailing, simply eliminate it */
  1171.   if right(tempdirspec,1)="." then
  1172.     verifydirspec=left(verifydirspec,length(verifydirspec)-1)
  1173.   else do
  1174.     /* if period is not trailing, it's "illegal" for Bink/Squish outbounds */
  1175.     if pos(".",tempdirspec)<>0 then do
  1176.       say "! "logdatetime()" "cmdname" ERROR: "errormessage" contains illegal period."
  1177.       call honk
  1178.       exit 1
  1179.     end
  1180.   end
  1181.   /* ensure that dir spec includes trailing backslash */
  1182.   verifydirspec=verifydirspec"\"
  1183.   return verifydirspec
  1184.  
  1185.  MAKEDIR:
  1186.    arg makedirspec
  1187.    if right(makedirspec,1)="\" then
  1188.      "md "left(makedirspec,length(makedirspec)-1)
  1189.    else
  1190.      "md "makedirspec
  1191.    makedirsave=directory()
  1192.    if \changedir(makedirspec) then do
  1193.      makedirsave=directory(makedirsave)
  1194.      return 0
  1195.    end
  1196.    else do
  1197.      makedirsave=directory(makedirsave)
  1198.      return 1
  1199.    end
  1200.  
  1201.  WRITELOG:
  1202.   parse arg writelogfile, writelogtype, writeloginfo
  1203.   if length(writelogtype)=0 then do
  1204.     say ""
  1205.     writelogtemp=lineout(writelogfile,"")
  1206.   end
  1207.   else do
  1208.     writelogtemp=writelogtype" "logdatetime()" "cmdname" "writeloginfo
  1209.     say writelogtemp
  1210.     if writelogfile<>"" then do
  1211.       writelogtemp=lineout(writelogfile,writelogtemp)
  1212.     end
  1213.   end
  1214.   return
  1215.  
  1216.  /* ------------------------------------------------------------------------
  1217.     "CALLed" routines and functions not specific to FFMB
  1218.     ------------------------------------------------------------------------ */
  1219.  
  1220.  CHANGEDIR: /* changes to passed directory */
  1221.   arg dirtochangeto
  1222.   dirtochangeto=strip(dirtochangeto)
  1223.   if right(dirtochangeto,1)="\" then do /* new dir has a trailing backslash */
  1224.     changedirtemp=directory(left(dirtochangeto,length(dirtochangeto)-1))
  1225.     if changedirtemp<>"" then
  1226.       return 1
  1227.     else
  1228.       return 0
  1229.   end
  1230.   else do /* dirtochangeto does not contain a trailing backslash */
  1231.     changedirtemp=directory(dirtochangeto)
  1232.     if changedirtemp<>"" then
  1233.       return 1
  1234.     else
  1235.       return 0
  1236.   end
  1237.  
  1238.  CLOSEFILE:
  1239.   arg closefilefile
  1240.   closefiletest=stream(closefilefile,"C","Close")
  1241.   if left(closefiletest,5)<>"READY" then
  1242.     return 0
  1243.   else
  1244.     return 1
  1245.  
  1246.  FINDFILE:
  1247.   arg findfilename
  1248.   if stream(findfilename,"C","Query exists")<>"" then
  1249.     return 1
  1250.   else
  1251.     return 0
  1252.  
  1253.  LOGDATETIME:
  1254.   td=substr(date(),1,6)
  1255.   if substr(td,6,1)=" " then td="0"substr(td,1,5)
  1256.   tdt=td" "time(N)
  1257.   return tdt
  1258.  
  1259.  MOVEFILE:
  1260.   /* Moves files (removes leading drive in dest. file) */
  1261.   /* NOTES:                                 */
  1262.   /* - This routine is (currently) limited to moving files around on the */
  1263.   /*   SAME drive!                             */
  1264.   /* - This routine does NOT check to see if destination file already     */
  1265.   /*   exists.                                 */
  1266.   arg origmovefile, destmovefile
  1267.   destmovefile=filespec("P",destmovefile)""filespec("N",destmovefile)
  1268.   "move "origmovefile" "destmovefile" > nul"
  1269.   return
  1270.  
  1271.  OLDFILE:
  1272.   /* returns file number of days old */
  1273.   /* WARNING: This routine is NO GOOD after 2069! */
  1274.   arg oldfilespec
  1275.   /* starting at third char, dom indicates days in year for previous months */
  1276.   dom="  000031059090121151182212243274304335"
  1277.   oldfiledate=stream(oldfilespec,"C","Query datetime")
  1278.   month=left(oldfiledate,2)
  1279.   day=substr(oldfiledate,4,2)
  1280.   year=substr(oldfiledate,7,2)
  1281.   /* file date stamps don't include centuries, so assume that if less than */
  1282.   /* 70, then add 100 (file years are 1970-2069)               */
  1283.   if year<70 then
  1284.     year=100+year
  1285.   fileday=693959+((year-1)*365)+((year-1)%4)-((year-1)%100)+((year-1)%400)+((month>2)&(year//4=0))-(year//100=0)+(year//400=0)+(substr(dom,month*3,3))+day
  1286.   return date("B")-fileday
  1287.   /*                                         */
  1288.   /* fileday construction (uses same rules as REXX's date("B") function):    */
  1289.   /*                                         */
  1290.   /*  693959              days to 1900                     */
  1291.   /*  + ((year-1)*365)          previous year X days/year             */
  1292.   /*  + ((year-1)%4)          every 4th, leap year                 */
  1293.   /*  - ((year-1)%100)          every 100th, non-leap year             */
  1294.   /*  + ((year-1)%400)          every 400th, leap year             */
  1295.   /*  + ((month>2)&(year//4=0))   if 4th and greater than Feb, leap year     */
  1296.   /*  - (year//100=0)          if cent, this is NON leap year         */
  1297.   /*  + (year//400=0)          if 4th cent, this is leap year         */
  1298.   /*  + (substr(dom,month*3,3))   days in previous months             */
  1299.   /*  + day              day number                     */
  1300.  
  1301.  OPENAPPEND:
  1302.   arg openappendfile
  1303.   openappendtest=stream(openappendfile,"Command","Open write")
  1304.   if left(openappendtest,5)<>"READY" then
  1305.     return 0
  1306.   else do
  1307.     openappendtest=stream(openappendfile,"Command","Seek < 0")
  1308.     return 1
  1309.   end
  1310.  
  1311.  OPENREAD:
  1312.   arg openreadfile
  1313.   openreadtest=stream(openreadfile,"Command","Open read")
  1314.   if left(openreadtest,5)<>"READY" then
  1315.     return 0
  1316.   else
  1317.     return 1
  1318.