home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / chkorpan.zip / CheckOrphans.txt
Text File  |  1998-02-15  |  11KB  |  357 lines

  1. /*********************************************************************
  2.  
  3. USAGE:
  4. -----
  5.  
  6.   Put this REXX script in a file -- e.g. CheckOrphans.CMD -- in your
  7.   Southsoft\PMINews directory. (Make sure that the comment line
  8.   `/* Report/Delete ... */' is the first line of the file.)
  9.  
  10.   Make sure PMINews isn't running.
  11.  
  12.   Run this as:
  13.  
  14.     CHECKORPHANS gldirspec [/DELETE]
  15.  
  16.   where:
  17.  
  18.     CHECKORPHANS  substitute whatever you called the .CMD file
  19.  
  20.     gldirspec     __.GL grouplist directory to process
  21.                   Wildcard pattern permitted to process multiple dirs.
  22.                   The `.GL' extension may optionally be omitted.
  23.  
  24.     /DELETE       Delete the orphan files.
  25.                   If this parameter is omitted, the program will only
  26.                   report the orphan files without deleting them.
  27.  
  28.   Example: To process all grouplist directories and perform deletion:
  29.  
  30.     CD \SOUTHSOFT\PMINEWS
  31.     CHECKORPHANS * /DELETE
  32.  
  33.  
  34. WHAT THIS PROGRAM DOES:
  35. ----------------------
  36.  
  37.   This inspects the GL.DAT file(s) for one or more grouplists in order
  38.   to determine what actual article-body files (they have names like
  39.   `EBMLK50.ART' etc.) are referenced in the grouplist(s). It then
  40.   reports/deletes any files that are not referenced ("orphans"). It
  41.   does not modify the GL.DAT(s) or any other PMINews data/information.
  42.  
  43.   When PMINews crashes, the .ART article files it's created are not
  44.   recorded in GL.DAT (this may apply to all cases or only some cases;
  45.   I'm not sure). So every time this occurs you get more and more of
  46.   them (inaccessible but presumably never to be deleted) cluttering up
  47.   the __.GL directories. Version 1.01A is more stable, but there are
  48.   still crashes. Also, even if stable, there may still be junk left
  49.   around from crashes in earlier versions. This program may be useful
  50.   for both purposes, and may help some people to avoid sledgehammer
  51.   cleanup actions (deleting whole grouplist, reinstalling, etc.)
  52.  
  53.   I think `Article Error' may also cause orphans (?).
  54.  
  55.   This also reports any .ART files that are referenced by GL.DAT but
  56.   which do not exist. (I haven't observed any actual errors of this
  57.   type in practice, however.)
  58.  
  59.   A more-sophisticated approach would be to fix GL.DAT to include the
  60.   missing references. This was beyond what I wanted to bother doing.
  61.   Also, it requires checking that each orphan file isn't a duplicate
  62.   of another article-body file; such duplicates occur for any articles
  63.   you download after a crash that it's already downloaded previously
  64.   but has now forgotten about. (In particular, the sequence "download
  65.   new article bodies" + crash + re-"download new article bodies" will
  66.   create numerous duplicate orphans.)
  67.  
  68.  
  69. DISCLAIMERS:
  70. -----------
  71.  
  72.   Use at your own risk! This has been tested on my system with my
  73.   PMINews database and PMINews 1.01A, and seems to work fine. Your
  74.   mileage may vary. Before running this on a __.GL directory, first
  75.   copy all its *.ART files to some temporary save dir, just in case.
  76.  
  77.   Future changes to the PMINews database organization or GL.DAT file
  78.   format may render this program incorrect.
  79.  
  80.   There may also be some crosschecking with GL.LVS that should also be
  81.   done. I did not examine this aspect. However, the newsgroups seem to
  82.   look fine in PMINews after running this.
  83.  
  84.   I'm by no means a PMINews expert -- I just poked around enough to
  85.   make something that seems to work. If there are any misassumptions
  86.   or errors in my approach, please let me know and I'll correct them.
  87.  
  88.  
  89. AUTHOR:
  90. ------
  91.  
  92.   Ron Poirier <rpoirier@unixg.ubc.ca>
  93.   1997-07-27
  94.  
  95.   Permission granted to copy or redistribute for personal use.
  96.  
  97. *********************************************************************/
  98.  
  99.  
  100. /* Report/Delete orphaned PMIMail .ART files in _.GL dir(s) */
  101.  
  102. /*********************************************************************/
  103.  
  104. /* REXX */
  105. CALL RxFuncAdd "SysLoadFuncs", "REXXUTIL", "SysLoadFuncs"
  106. CALL SysLoadFuncs
  107.  
  108.  
  109. /**** Program parameters ****/
  110.  
  111. PARSE UPPER ARG glfilespec deleteopt
  112.  
  113. IF deleteopt = "" THEN DoDelete="N"
  114. ELSE IF deleteopt = "/DELETE" THEN DoDelete="Y"
  115. ELSE DO
  116.     SAY "Invalid parameter: `" || deleteopt || "'"
  117.     SIGNAL SyntaxError
  118.     END
  119.  
  120. IF glfilespec = "" THEN DO
  121.     SAY "Missing filespec parameter (grouplist directory(s) to process)"
  122.     SIGNAL SyntaxError
  123.     END
  124. ELSE IF left(glfilespec,1) = "/" THEN DO
  125.     SAY "Invalid parameter: `" || glfilespec || "'"
  126.     SIGNAL SyntaxError
  127.     END
  128.  
  129. IF right(glfilespec,3) \= ".GL" THEN glfilespec = glfilespec || ".GL"
  130.  
  131.  
  132. ClrLine = "0D1B"X || "[K"
  133. UpLine = "1B"X || "[1A"
  134.  
  135.  
  136. /**** Get names of all matching .GL directories ****/
  137.  
  138. CALL SysFileTree glfilespec, gldirs., 'DO'
  139.  
  140. IF gldirs.0 = 0 THEN DO
  141.     SAY "No _.GL grouplist directories found matching `" || glfilespec || "'"
  142.     /* See if there really aren't any _.GL dirs or if they're not
  143.     being found because of wrong path: */
  144.     t = filespec("drive",glfilespec) || filespec("path",glfilespec) ,
  145.         || "PMINEWS.EXE"
  146.     IF stream(t,"C","QUERY EXISTS") = "" THEN DO
  147.         SAY "_.GL subdirectories are in the ...\PMINEWS directory."
  148.         SAY "CD to ...\PMINEWS directory to run this or specify a full path"
  149.         END
  150.     EXIT
  151.     END
  152.  
  153. SAY "Grouplist directories to be processed:"
  154. DO i = 1 TO gldirs.0
  155.     SAY " " filespec("name",gldirs.i)
  156. END i
  157.  
  158.  
  159. DO gldi = 1 to gldirs.0
  160.  
  161.  
  162. cd = gldirs.gldi;  cdn = filespec("name",cd)
  163. gldat = cd || "\GL.DAT"
  164.  
  165. SAY ""
  166. SAY "Processing grouplist dir" cdn "..."
  167.  
  168.  
  169. /**** Build list of what .ART files are referenced by GL.DAT file ****/
  170.  
  171. /* format of each GL.DAT entry line is apparently:
  172.      ($ = 'FF'X delimiter)
  173.    seq#  $                  apparently internal PMINews article seq#
  174.    readstatus(0 or 1)  $    I think
  175.    <msgid>  $
  176.    yy-mm-dd  $
  177.    hh:mm:ss  $
  178.    subject  $
  179.    fromID  $
  180.    fromName  $
  181.    ????  $                  ?? (seems to always be 'FE'X ???)
  182.    #lines  $
  183.    _.GL\ARTICLES\xxxxxxxx.ART  $  article-body filename, or (apparently)
  184.                                   'FE'X => none (body not stored locally)
  185.    replytoID/Name (e.g., `me@my.com (Me)')  $
  186.    Xref:...  $
  187.    #  $                     ??
  188. */
  189.  
  190. IF stream(gldat, "C", "OPEN READ") \= "READY:" THEN DO
  191.     SAY "Could not open GL.DAT file" gldat
  192.     ITERATE gldi
  193.     END
  194.  
  195. CALL stream gldat, "C", "SEEK = 1"
  196.  
  197. /* Read header line (count) just to crosscheck. */
  198. PARSE VALUE linein(gldat) WITH GLnentries
  199. CALL linein gldat  /* skip blank line */
  200.  
  201. CALL charout "con", "Inspecting GL.DAT ..."
  202.  
  203. nfart = 0     /* build list in fart of referenced .ART files */
  204. nentries = 0  /* # of article entries found in GL.DAT */
  205.  
  206. DO WHILE lines(gldat) \= 0
  207.     text = linein(gldat)
  208.     text = translate(text, '7F20'X, '20FF'X) /*kludge to make PARSE work*/
  209.     PARSE VALUE text WITH seq rdstatus msgID dt tm ,
  210.       subj uid uname somethingorother nlines fn .
  211.     /*debug
  212.       SAY "seq="seq "rdstatus="rdstatus "msgID="msgID ,
  213.       "timestamp="dt","tm "subj=`"subj"'" "fromID="uid ,
  214.       "fromName="uname "lines="nlines "file="fn
  215.       PULL dummy
  216.     */
  217.     IF fn \== 'FE'X THEN DO
  218.         nfart = nfart + 1
  219.         fart.nfart = filespec("name",fn)
  220.         END
  221.     nentries = nentries + 1
  222. END /* DO WHILE */
  223.  
  224. CALL charout "con", ClrLine
  225. SAY "..." nfart ".ART files referenced"
  226.  
  227. IF nentries \= GLnentries THEN DO
  228.     /* I haven't seen this error in practice */
  229.     SAY "... OOPS! found" nentries "article entries in GL.DAT"
  230.     SAY "    but header line in GL.DAT claims" GLnentries
  231.     END
  232.  
  233. CALL stream gldat, "C", "CLOSE"
  234.  
  235.  
  236. /**** Get list of actual .ART files in directory ****/
  237.  
  238. CALL charout "con", "Inspecting directory ..."
  239.  
  240. filespec = cd || "\ARTICLES\*.ART"
  241.  
  242. CALL SysFileTree filespec, art., 'FO'
  243.  
  244. nart = art.0
  245.  
  246. CALL charout "con", ClrLine
  247. SAY "..." nart ".ART files found"
  248.  
  249.  
  250. n_errs = 0
  251.  
  252.  
  253. IF nart \= 0 | nfart \= 0 THEN DO
  254.  
  255.  
  256.     IF nart = nfart THEN SAY "Probably no errors; checking anyway ..."
  257.  
  258.  
  259.     /**** Identify orphan files not referenced by GL.DAT ****/
  260.  
  261.     SAY "Crosschecking for reference errors ..."
  262.  
  263.     DO i = 1 TO nart
  264.         artmark.i = 0  /* init all to unreferenced */
  265.     END i
  266.  
  267.     n_missing = 0
  268.  
  269.     DO i = 1 TO nfart
  270.         /* key from list of referenced files; mark each one as
  271.         referenced in actual-file list */
  272.         j = binsrch(fart.i)
  273.         IF j=0 THEN DO
  274.             SAY ">>>> Aaack! Referenced file" fart.i "was NOT found!"
  275.             CALL charout "con", "Press RETURN to continue..."
  276.             dummy = linein("con")
  277.             n_missing = n_missing + 1
  278.             ITERATE
  279.             END
  280.         artmark.j = 1  /* mark this file as being referenced */
  281.     END i
  282.  
  283.     /**** Report/Delete all the unreferenced ones ****/
  284.  
  285.     n_unref = 0
  286.     DO i = 1 TO nart
  287.         /* show/delete each one marked as unreferenced */
  288.         IF artmark.i=0 THEN DO
  289.             n_unref = n_unref + 1
  290.             IF DoDelete="N" THEN DO
  291.                 SAY ">>> Unreferenced file" filespec("name",art.i)
  292.                 END
  293.             ELSE DO
  294.                 SAY ">>> Deleting unreferenced file" filespec("name",art.i)
  295.                 '@DEL "' || art.i || '"'
  296.                 END
  297.             END
  298.     END i
  299.  
  300.     IF n_unref \= 0 & DoDelete = "Y" THEN dd=" deleted"; ELSE dd=""
  301.     SAY "..." n_unref "unreferenced orphan .ART files"||dd "in dir" cdn
  302.  
  303.     SAY "..." n_missing "referenced-but-missing .ART files in dir" cdn
  304.  
  305.     n_errs = n_unref + n_missing
  306.  
  307.     IF n_unref \= nart-nfart+n_missing THEN DO
  308.         SAY "Oops, self-check failure: n_unref \= nart-nfart+n_missing"
  309.         END
  310.  
  311.  
  312. END /* DO IF nart\=0 | nfart\=0 */
  313.  
  314.  
  315. IF n_errs=0 THEN ,
  316.     SAY "No errors found by this program for dir" cdn
  317. ELSE SAY n_errs "errors found by this program for dir" cdn
  318.  
  319. IF gldi < gldirs.0 THEN DO  /* more dirs to process */
  320.     CALL charout "con", "Press RETURN to continue..."
  321.     dummy = linein("con")
  322.     CALL charout "con", UpLine||ClrLine
  323.     END
  324.  
  325.  
  326. END gldi /* DO loop through each _.GL grouplist dir */
  327.  
  328.  
  329. EXIT
  330.  
  331.  
  332.  
  333. binsrch: PROCEDURE EXPOSE art. nart; ARG f
  334.  
  335.     lo = 1;  hi = nart;
  336.     DO WHILE lo <= hi
  337.         mid = (hi+lo)%2
  338.         mf = filespec("name",art.mid)
  339.         IF f>mf THEN lo=mid+1
  340.         ELSE IF f<mf THEN hi=mid-1
  341.         ELSE RETURN mid
  342.     END /* DO WHILE */
  343.     RETURN 0
  344.  
  345.  
  346. SyntaxError:
  347.     SAY "Usage:"
  348.     PARSE SOURCE . . myname
  349.     pmyname = filespec("name",myname)
  350.     pmyname = left(pmyname,length(pmyname)-4) /* remove `.CMD' */
  351.     SAY " " pmyname "GLdirfilespec [/DELETE]"
  352.     EXIT
  353.  
  354. EXIT
  355. /* All Done */
  356.  
  357.