home *** CD-ROM | disk | FTP | other *** search
/ Beijing Paradise BBS Backup / PARADISE.ISO / software / BBSDOORW / DWNSRS56.ZIP / DOWNCOL.C next >
Encoding:
C/C++ Source or Header  |  1993-02-10  |  42.0 KB  |  962 lines

  1. /* ============================================================= */
  2. /*  Rob Hamerling's MAXIMUS download file scan and sort utility. */
  3. /*  -> DOWNCOL.C                                                 */
  4. /*  -> Functions to collect download file information.           */
  5. /* ============================================================= */
  6.  
  7. #define INCL_DOS
  8. #define INCL_DOSFILEMGR
  9. #define INCL_DOSERRORS
  10. #define INCL_NOPMAPI
  11. #include <os2.h>
  12.  
  13. #include <conio.h>
  14. #include <fcntl.h>
  15. #include <io.h>
  16. #include <malloc.h>
  17. #include <share.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <sys\types.h>
  22.  
  23. #include "..\max\mstruct.h"             // MAXIMUS definitions
  24. #include "downsort.h"                   // downsort defines
  25. #include "downfpro.h"                   // downsort function prototypes
  26.  
  27.  
  28. /* prototypes of local function functions */
  29.  
  30. void    add_to_chain(struct _filechain **cp, struct _filechain *tp);
  31. int     area_selection(char *, char [][MAXANAME]);
  32. struct _filechain *add_comment(char *, struct _downpath huge *,
  33.                                char *, short int, unsigned short int);
  34. struct _filechain *assign_desc(short int, char *, struct _filechain **,
  35.            struct _downpath huge *, char *, short int, unsigned short int);
  36. char *build_desc(FILE *, char *, char **);
  37. unsigned short int combine_comments(void);
  38. struct _filechain *desc_dup(struct _filechain *, char *,
  39.                struct _downpath huge *, short int, unsigned short int,
  40.                unsigned short int, unsigned short int);
  41. void  desc_old(struct _filechain **, char *, struct _downpath huge *,
  42.                short int, short int, unsigned short int,
  43.                unsigned short int, unsigned short int);
  44. unsigned short int fill_chn(struct _downpath huge *, struct _filechain **);
  45. struct _filechain *file_element(struct _downpath huge *,
  46. #ifndef __32BIT__
  47.                                 struct _FILEFINDBUF *);
  48. #else
  49.                                 struct _FILEFINDBUF3 *);
  50. #endif
  51. unsigned short int free_orphan(void);
  52. unsigned short int get_desc(struct _downpath huge *, struct _filechain **);
  53. char *parse_fname(char *);
  54. int   sort_path(const void *, const void *);
  55.  
  56.  
  57. /* ---------------------------- */
  58. /* Collect download file area's */
  59. /* ---------------------------- */
  60. unsigned short int collect_area(struct _downpath huge **area)
  61. {
  62. #ifndef __32BIT__
  63.   unsigned short int
  64. #else
  65.   unsigned long int
  66. #endif
  67.          af, oaction;                   // file handle, open_action
  68.                                         /* short in 286, long in 386 mode */
  69.   int     rc;                           // returncode
  70.   short   int i,j,k,m;                  // counters
  71.   struct _area a;                       // MAXIMUS (minimum) area struct.
  72.   struct _downpath huge *d;             // ptr to array with area-info
  73.   struct _FILESTATUS fs;                // file status buffer
  74.  
  75.   rc = DosOpen(areadat_path,            // open 'carefully'
  76.           &af,                          // pointer to File Handle
  77.           &oaction,                     // pointer to action field
  78.           0L,                           // new filesize (N/A)
  79.           FILE_NORMAL,                  // attributes
  80.           FILE_OPEN,                    // open flags
  81.           OPEN_ACCESS_READONLY    |     // mode
  82.             OPEN_SHARE_DENYWRITE,
  83.           0L);                          // no EA's
  84.   if (rc != NO_ERROR) {                 // open error
  85.     printf(OPEN_FAIL, areadat_path, rc);
  86.     DosExit(rc,0);
  87.     }
  88.   if (oaction != FILE_EXISTED) {        // not found
  89.     printf(OPEN_ACTN, areadat_path, oaction);
  90.     DosExit(5,0);
  91.     }
  92.   else {
  93.     if (oper_mode != QUIET)
  94.       printf("\nCollecting file-area information from %s",
  95.                 areadat_path);
  96.     rc = DosQFileInfo(af, 1, &fs, sizeof(fs)); // get info of AREA.DAT
  97.     read(af,(char *)&a,sizeof(struct _area));  // obtain first part
  98.     m = 0;                              // init participating file count
  99.     if (a.id != AREA_id) {              // validate MAXIMUS format
  100.       printf("\nSorry, %s-file has unsupported AREA.DAT format!",
  101.                        areadat_path);
  102.       printf("\n%s %c.%c%c works only for %s version 2.00 and up!",
  103.                  PROGNAME,VERSION,SUBVERS,SUFFIX,MAX);
  104.       }
  105.     else {                              // acceptable AREA.DAT
  106.       k = (short int)(fs.cbFile / a.struct_len);  // number of area's
  107.       for (i=0; i<k; i++) {             // count area's with downloads
  108.         lseek(af, i*(long)a.struct_len, SEEK_SET);  // locate to next
  109.         read(af, (char *)&a, sizeof(struct _area));
  110.         if (a.filepath[0] != '\0'       &&
  111.             a.filepath[0] != ' '        &&
  112.             a.filepriv <= ABS_MAX_priv  &&   // within report privilege
  113.             area_selection(a.name, selected_area))   // not excluded
  114.           m++;                          // participating filearea count
  115.         }
  116.       if (m > 0) {                      /* any area's participating */
  117. #ifndef __32BIT__
  118.         d = (struct _downpath huge *)halloc(m, sizeof(struct _downpath));
  119. #else
  120.         d = (struct _downpath *)malloc(m * sizeof(struct _downpath));
  121. #endif
  122.         if (d == NULL) {                // memory not obtained?
  123.           printf(MSG_MEM, PROGNAME);
  124.           DosExit(6,0);
  125.           }
  126.         else {                          // have memory for area-info
  127.           *area = d;                    // set return pointer
  128.           for (i=m=0; i<k; i++) {       // all area's
  129.             lseek(af, i*(long)a.struct_len, SEEK_SET);  // locate to next
  130.             read(af, (char *)&a, sizeof(struct _area));
  131.             if ( a.filepath[0] != '\0'      &&  // filename(?)
  132.                  a.filepath[0] != ' '       &&  // filename(?)
  133.                  a.filepriv <= ABS_MAX_priv &&  // within report privilege
  134.                  area_selection(a.name, selected_area)) {  // selected
  135.               d[m].priv = a.filepriv;           // download privilege
  136.               strcpy(d[m].name, a.name);        // area name ... edit!
  137.               d[m].anum = i;                    // AREA.DAT seq. nbr
  138.               d[m].file_count = 0;              // init file count
  139.               d[m].byte_count = 0L;             // init byte count
  140.               d[m].newest     = NULL;           // pointer to newest file
  141.               strcpy(d[m].pname, a.filepath);   // download path
  142.               j = strlen(d[m].pname);
  143.               if (j>0 && d[m].pname[j-1] != '\\')
  144.                 d[m].pname[j] = '\\';           // add backslash if needed
  145.               strcpy(d[m].filesbbs, a.filesbbs); // files.bbs
  146.               strcpy(d[m].adesc, a.fileinfo);   // filearea title
  147.               ++m;                              // filearea index
  148.               }
  149.             }
  150.           }
  151.         // manipulate here area-names for 'intelligent' sorting
  152.         max_aname = 0;                  // init max areaname length
  153.         for (i=0; i<m; i++)             // all collected area's
  154.           max_aname = max(strlen(d[i].name), max_aname);
  155.         for (i=0; i<m; i++) {           // all collected area's
  156.           sprintf(d[i].ename,
  157.                  (d[i].name[0]<'0' || d[i].name[0]>'9')  // not num
  158.                     ? "%-*.*s" : "%*.*s",  // left or right aligned
  159.                   max_aname, max_aname, d[i].name);
  160.           d[i].ename[max_aname] = '\0'; // end of string
  161.           j = k = max_aname-1;          // init offset values
  162.           while (j>=0 && d[i].ename[j] == ' ')  // search last char
  163.             j--;
  164.           if (j < k) {                  // spaces found
  165.             while (j>=0               &&
  166.                    d[i].ename[j]>='0' &&
  167.                    d[i].ename[j]<='9') {
  168.               d[i].ename[k--] = d[i].ename[j];  // move
  169.               d[i].ename[j--] = ' ';            // replace by blank
  170.               }
  171.             }
  172.           }
  173. #if defined(DEBUG)
  174.         dump_area_array(d,m,"check on ename editing");
  175. #endif
  176.         }
  177.       }
  178.  
  179.     DosClose(af);                       // close area.dat-file
  180.     return(m);                          // report number of downloadarea's
  181.     }
  182.   }
  183.  
  184. /* ---------------------------- */
  185. /* Collect all file information */
  186. /* ---------------------------- */
  187. unsigned int collect_file(unsigned int a,
  188.                           struct _downpath huge *area)
  189. {
  190.   unsigned short int  i,k,l,fc,orphan_count;   // counters
  191.   struct _filechain *ca,*ce;            // ptrs to file info
  192.  
  193.   fc = orphan_count = 0;                // init file and orphan count
  194.   if (oper_mode != QUIET)
  195.     printf("\nCollecting file data");
  196.   qsort(area, a, sizeof(struct _downpath), sort_path);
  197.                          // WARNING: do no sort again in this array!
  198.   for (i=0; i<a; i++) {                 // all download areas
  199.     if (oper_mode == VERBOSE)
  200.       printf("\n       files in DL-dir %s (area %s)",
  201.            area[i].pname,
  202.            area[i].name);
  203.     else if (oper_mode != QUIET)
  204.       printf(".");
  205.  
  206.     if (i<1 || stricmp(area[i].pname, area[i-1].pname)) {  // pathnames
  207.       if (lp[P_ORP].priv[0] > HIDDEN)   // orphan report not req'd
  208.         orphan_count += free_orphan();  // free orphans still in chain
  209.       ca = NULL;                        // for new directory
  210.       k = fill_chn(&area[i], &ca);      // read directory
  211.                                         // returns #files + ptr to first
  212.       fc += k;                          // add to total
  213.       if (ca!=NULL) {                   // any files in group
  214.         if ((ce = first_element) != NULL) {  // not empty chain
  215.            while (ce->next_element != NULL) // find last element
  216.              ce = ce->next_element;
  217.            ce->next_element = ca;       // couple new group to chain
  218.            }
  219.         if (first_element==NULL)        // first non-empty group
  220.           first_element = ca;           // set pointer to head of chain
  221.         }
  222.       }
  223.     if (oper_mode == VERBOSE)
  224.       printf("\r %5d",k);               // total this download path
  225.     l = get_desc(&area[i], &ca);        // obtain descriptions
  226.     fc += l;                            // add 'offline' filecount
  227.     if (first_element==NULL)            // very first
  228.       first_element = ca;               // set pointer to head of chain
  229.     if (l>0 && oper_mode==VERBOSE)      // any offline files found
  230.       printf("\r %5d", k += l);         // value after get_desc
  231.     }                                 // all area's processed
  232.  
  233.   if ((ca = first_element) != NULL) {   // any files
  234.     if (lp[P_ORP].priv[0] > HIDDEN) {   // orphan report not req'd
  235.       orphan_count += free_orphan();    // free any left orphans
  236.       if (ca->priv >= HIDDEN) {         // first is an orphan
  237.         first_element=ca->next_element; // new ptr to first
  238.         free(ca);                       // free memory block
  239.         ++orphan_count;                 // one more dropped
  240.         }
  241.       }
  242.     }
  243.   if (orphan_count>0 && oper_mode==VERBOSE)
  244.     printf("\n %5u orphans detected and dropped (in total)",
  245.               orphan_count);
  246.  
  247.   fc -= orphan_count;                   // less dropped orphans
  248.  
  249. #if defined(DEBUG)
  250.   dump_file_chain(first_element, " --> after all areas");
  251. #endif
  252.  
  253.   if (fc) {                             // file(s) present
  254.     k=0;                                // init count
  255.     if ((ca = first_element) != NULL) { // pointer to first element
  256.       ++k;                              // at least 1
  257.       while (ca->next_element != NULL) { // find last
  258.         ++k;                            // another element
  259.         ca = ca->next_element;          // pointer to next
  260.         }
  261.       }
  262.     if (fc != k) {                      // compare counts
  263.       printf("\nInternal error in data collection phase, "
  264.              "counted: %u entries, chainsize=%u",
  265.               fc, k);                   // counter values
  266.       DosExit(99,0);                    // quit on error
  267.       }
  268.     }
  269.   return(fc);                           // file total count
  270.   }
  271.  
  272. /* ---------------------------------------- */
  273. /* Add all subdir-filenames to the chain,   */
  274. /* for further processing by mainline.      */
  275. /* NOTE: - full path name assumed!          */
  276. /* returns number of files in this area     */
  277. /* ---------------------------------------- */
  278. unsigned short int fill_chn(struct _downpath huge *parea,
  279.                             struct _filechain **cp)
  280. {
  281.   int      rc;                          // returncode of DOS-calls
  282.   unsigned short int fc;                // area-file counter
  283.   struct _filechain *ca,*ce,*tp;        // new, curr, temp, chain ptrs
  284.   char   down_spec[MAXPATH];            // file specification buffer
  285.   HDIR   fhandle = HDIR_CREATE;         // FindFirst/Last handle
  286. #ifndef __32BIT__
  287.   unsigned int fentries;                // # entries to be retrieved
  288.   struct _FILEFINDBUF cf;               // file-info buffer
  289. #else
  290.   unsigned long int fentries;          // # entries to be retrieved
  291.   struct _FILEFINDBUF3 cf;              // file-info buffer
  292. #endif
  293.  
  294.   fc = 0;                               // init filecount of this area
  295.   fentries = 1;                         // retreive 1 at a time (DOS!)
  296.   strcpy(down_spec,parea->pname);       // path
  297.   strcat(down_spec,"*.*");              // file-spec
  298.   rc = DosFindFirst(down_spec,
  299.                     &fhandle,
  300.                     FILE_NORMAL,
  301.                     &cf,
  302. #ifndef __32BIT__
  303.                     sizeof(struct _FILEFINDBUF),
  304.                     &fentries,
  305.                     0L);                // unused with OS/2 < 2.0
  306. #else
  307.                     sizeof(struct _FILEFINDBUF3),
  308.                     &fentries,
  309.                     FIL_STANDARD);      // no EA's OS/2 2.0+
  310. #endif
  311.   ca = NULL;                            // no first in group yet
  312.   while (rc == 0) {
  313.     if ((wild_comp(cf.achName,"FILES.*")     != 0) &&
  314.         (wild_comp(cf.achName,"*.BAK")       != 0) &&
  315.         (wild_comp(cf.achName,"SYSTEM*.?BS") != 0) &&
  316.         (wild_comp(cf.achName,"DIR.?BS")     != 0)) {
  317.  
  318.       tp = file_element(parea, &cf);    // add new element to chain
  319.                                         // note: exits if no memory
  320.       if (ca == NULL)                   // it is first element in group
  321.         ca = ce = tp;                   // first and last in group
  322.       else {                            // not first
  323.         ce->next_element = tp;          // chain-ptr in previous
  324.         ce = tp;                        // new last in group
  325.         }
  326.       ++fc;                             // update filecount this area
  327.       byte_count += cf.cbFile;          // update file bytecount
  328.  
  329.       if (oper_mode==VERBOSE && (fc%25)==0)  // every 25 files
  330.         cprintf("\r %5u",fc);
  331.       }
  332.     fentries = 1;                       // retreive 1 at a time (DOS!)
  333.     rc = DosFindNext(fhandle,
  334.                      &cf,
  335. #ifndef __32BIT__
  336.                      sizeof(struct _FILEFINDBUF),
  337.                      &fentries);
  338. #else
  339.                      sizeof(struct _FILEFINDBUF3),
  340.                      &fentries);
  341. #endif
  342.     }
  343.   DosFindClose(fhandle);                // close directory association
  344.   *cp = ca;                             // pointer to first new this area
  345.   return(fc);
  346.   }                                     // return # of allocated elements
  347.  
  348. /* -------------------------------------- */
  349. /* Add file description to chian elements */
  350. /* -------------------------------------- */
  351. unsigned short int get_desc(struct _downpath huge *parea,
  352.                             struct _filechain **cp)
  353. {
  354. #ifndef __32BIT__
  355.   unsigned short int
  356. #else
  357.   unsigned long int
  358. #endif
  359.           fih,                          // file handle
  360.           oaction;                      // open-action flags
  361.   FILE   *fi;                           // file pointer
  362.   unsigned short int y;                 // number of offline files
  363.   struct _filechain *ce, *tp;           // ptrs to file-info
  364.   char   buf[MAXRCD];                   // read-buffer for FILES.BBS
  365.   char   filename[MAXFN];               // filename from FILES.BBS buf
  366.   char   *desc, *rp;                    // ptr to desc / return val gets()
  367.   short int  i,k,m,p,x;                 // counters
  368.   short int  fpriv;                     // file privilege
  369.   int      rc;                          // returncode
  370.   unsigned short int bbsseq;            // current line number of FILES.BBS
  371.   char   desc_spec[MAXPATH];
  372.   struct _filechain **fa;               // pointer to file sort-array
  373.   static char prop[4]    = "/|\\-";     // rotating propeller
  374.  
  375.   if (strlen(parea->filesbbs) > 0)      // explicit specification?
  376.     strcpy(desc_spec,parea->filesbbs);  // explicitly specified FileList
  377.   else {                                // no
  378.     strcpy(desc_spec,parea->pname);     // get path to download directory
  379.     strcat(desc_spec,lp[P_FIL].name);   // add filename
  380.     strcat(desc_spec,".");              // add separator
  381.     strcat(desc_spec,lp[P_FIL].ext);    // add extension
  382.     }
  383.  
  384.   fpriv = parea->priv;                  // area-priv is default file priv
  385.                                         // FILES.BBS may be in use!!!
  386.   rc = DosOpen(desc_spec,               // Open 'carefully'
  387.           &fih,                         // pointer to File Handle
  388.           &oaction,                     // pointer to action field
  389.           0L,                           // new filesize (N/A)
  390.           FILE_NORMAL,                  // attributes
  391.           FILE_OPEN,                    // open flags
  392.           OPEN_ACCESS_READONLY    |     // mode
  393.             OPEN_SHARE_DENYWRITE,
  394.           0L);                          // no EA's OS/2 2.0+
  395.   if (rc) {                             // open error
  396.     printf(OPEN_FAIL, desc_spec, rc);
  397.     return(0);                          // no: return to caller
  398.     }
  399.   if ( (fi = fdopen(fih, "r")) == NULL) { // open now as stream
  400.     printf(OPEN_FAIL, desc_spec, 0);    // dummy rc
  401.     return(0);                          // no: return to caller
  402.     }
  403.  
  404.   ce = *cp;                             // ptr to 1st element of group
  405.   for (x=0; ce != NULL; x++)            // count collected dir entries
  406.     ce = ce->next_element;
  407.  
  408.   if (x > 0) {                          // if any files in dir
  409.     fa = (struct _filechain **)malloc(x * sizeof(struct _filechain *));
  410.     if (fa == NULL) {                   // not enough memory
  411.       printf(MSG_MEM,PROGNAME);
  412.       DosExit(11,0);
  413.       }
  414.     ce = *cp;                           // ptr to 1st element of group
  415.     for (i=0; ce != NULL; i++) {        // init sort array
  416.       fa[i] = ce;                       // element pointer into array
  417.       ce = ce->next_element;
  418.       }
  419.     qsort(fa, x, sizeof(struct _filechain *), sort_gbl);
  420.     }                                   //
  421.   else                                  // no files in group
  422.     fa=NULL;                            // no array allocated
  423.  
  424.   y = 0;                                // init 'offline' counter
  425.   bbsseq = 0;                           // init counter
  426.   rp = fgets(buf, MAXDESC, fi);         // get first FILES.BBS record
  427.   while (rp != NULL) {                  // until end of FILES.BBS
  428.     bbsseq++;                           // line counter
  429.     m = strlen(buf);                    // length of input string
  430.     if (m>1 && buf[0] == '\20') {       // privilege change (^P) ?
  431.       for (k=0; k<HIDDEN-TWIT && priv_name[k][0]!=buf[1]; k++);
  432.       if (TWIT + k > parea->priv)       // only if higher than area-priv
  433.         fpriv = TWIT + k;               // new (higher) prilivege
  434.       rp = fgets(buf, MAXDESC, fi);     // continue
  435.       }
  436.     else if (parse_fname(buf) == NULL) { // NOT likely a filename
  437.       if (bbsseq > 8                  &&    // past 'standard' header
  438.         (lp[P_ALL].sortflag == KEEPSEQ ||   // ALL-list or
  439.          lp[P_IPF].sortflag == KEEPSEQ ||   // IPF-list or
  440.          lp[P_IP2].sortflag == KEEPSEQ ||   // IP2-list or
  441.          (lp[P_FIL].sortflag == KEEPSEQ &&
  442.           lp[P_FIL].priv[0] <= HIDDEN))) {  // FIL-list req'd with '/K'
  443.         tp = add_comment(EMPTY, parea,     // filename = null-string
  444.                      buf, fpriv, bbsseq);
  445.         add_to_chain(cp, tp);           // add element to end-of-chain
  446.         ++y;                            // add 1 to 'offline' filecount
  447.         }
  448.       rp = fgets(buf, MAXDESC, fi);     // continue
  449.       }
  450.     else {                              // probably file descr record
  451.       for (i=0; i < MAXFN-1     &&      // maximum filename length (8.3)
  452.                 i < m           &&      // string length
  453.                 buf[i] != ' '   &&      // space character
  454.                 buf[i] != '\r'  &&      // CR character
  455.                 buf[i] != '\n';         // LF character
  456.                    i++);                // scan for filespec
  457.       strncpy(filename, buf, i);        // save filename
  458.       filename[i] = '\0';               // end of string
  459.       desc = build_desc(fi, buf, &rp);  // build description string
  460.       if (desc == NULL)                 // no description
  461.         rp = fgets(buf, MAXDESC, fi);   // next record
  462.       tp = assign_desc(x, filename,     // assign desc to file(s)
  463.                     fa, parea, desc, fpriv, bbsseq);
  464.       if (tp != NULL) {                 // new element for group
  465.         add_to_chain(cp, tp);           // add element to end-of-chain
  466.         ++y;                            // add 1 to offline filecount
  467.         }
  468.       }
  469.     if (oper_mode == VERBOSE && (bbsseq%5) == 0)
  470.       cprintf("\r%c",prop[p=(++p)&3]);  // propeller
  471.     }
  472.   if (fa != NULL)                       // memory for array allocated
  473.     free(fa);                           // free temp sort array
  474.   x = combine_comments();               // drop comment pairs
  475.   if (oper_mode == VERBOSE)
  476.     cprintf("\r ");                     // clear propeller
  477.   fclose(fi);                           // finished with this FILES.BBS
  478.   DosClose(fih);                        // close the handle
  479.   return(y-x);                          // return 'offline' filecount
  480.                                         // (includes comment elements)
  481.   }
  482.  
  483. /* -------------------------------------- */
  484. /* Add a new file element to end-of-chain */
  485. /* -------------------------------------- */
  486. void add_to_chain(struct _filechain **cp,
  487.                   struct _filechain *tp)
  488. {
  489.   struct _filechain *ce;                // work pointer
  490.  
  491.   if (*cp == NULL) {                    // group was empty
  492.     *cp = tp;                           // return ptr to first in group
  493.     if (first_element == NULL)          // whole chain empty
  494.       return;                           // ===> nothing else to do here!
  495.     else                                // something in chain
  496.       ce = first_element;               // ptr to very 1st element
  497.     }
  498.   else
  499.     ce = *cp;                           // pointer to 1st in group
  500.  
  501.   while (ce->next_element != NULL)      // search last element
  502.     ce = ce->next_element;
  503.   ce->next_element = tp;                // add new element to chain
  504.   }
  505.  
  506. /* ------------------------------------- */
  507. /* Build file description memory block   */
  508. /* ------------------------------------- */
  509. char *build_desc(FILE  *fi,
  510.                  char  *ibuf,
  511.                  char  **rp)
  512. {
  513.   char   *fd;                           // pointer memory block with desc
  514.   char   *desc;                         // string work pointers
  515.   char   buf2[MAXDESC];                 // work buffers
  516.   unsigned short int k;                 // counter
  517.  
  518.   desc = next_word(ibuf);               // locate description
  519.   if (desc != NULL) {                   // start of description found
  520.     for (k=0; desc[k]; ++k) {           // search first NL/CR
  521.       if (desc[k]=='\r' || desc[k]=='\n') {
  522.         desc[k] = '\0';
  523.         break;
  524.         }
  525.       }
  526.     strcpy(buf2, desc);                 // save desc in buf2
  527.     while (((*rp = fgets(ibuf, MAXDESC, fi)) != NULL) &&  // next rcd
  528.            (ibuf[0] == ' ') &&          // probably desc continuation
  529.            ((desc = next_word(ibuf)) != NULL)) {
  530.       for (k=0; desc[k]!='\0'; ++k) {
  531.         if (desc[k]=='\r' || desc[k]=='\n') {
  532.           desc[k] = '\0';
  533.           break;
  534.           }
  535.         }
  536.       if ((strlen(buf2) + k + 1) <= sizeof(buf2)) {  // not too long
  537.         strcat(buf2," ");               // single space
  538.         strcat(buf2, desc);             // concat desc to buf2
  539.         }
  540.       }
  541.     fd = malloc(strlen(buf2)+1);        // obtain memory
  542.     if (fd == NULL) {
  543.       printf(MSG_MEM,PROGNAME);         // not enough memory
  544.       DosExit(12,0);
  545.       }
  546.     else
  547.       strcpy(fd, buf2);                 // store description
  548.     }
  549.   else                                  // no description
  550.     fd = NULL;                          // return value
  551.   return(fd);                           // return with pointer
  552.   }
  553.  
  554. /* ------------------------------------- */
  555. /* Assign description to file element(s) */
  556. /* ------------------------------------- */
  557. struct _filechain *assign_desc(short int x,
  558.                                char  *filename,
  559.                                struct _filechain **fa,
  560.                                struct _downpath huge *parea,
  561.                                char   *desc,
  562.                                short  int fpriv,
  563.                                unsigned short int bbsseq)
  564. {
  565.   short int    low,high,index;          // indexes
  566.   short int    b,n;                     // counters
  567.   unsigned short int dlb,dlt;           // download flags in description
  568.   struct _filechain *tp;                // ptr to file-info
  569.   char         dl_flags[5];             // copy of first part description
  570.  
  571.   dlb = dlt = 0;                        // flags off
  572.   if (desc != NULL) {                   // only when real descr. present
  573.     strncpy(dl_flags, desc, 4);         // copy part of description
  574.     if (dl_flags[0] == '/') {           // could be valid dl_flag
  575.       strupr(dl_flags);                 // make all uppercase
  576.       if (dl_flags[1] == 'B' || dl_flags[2] == 'B')
  577.         dlb = 1;                        // unlimited download bytes flag
  578.       if (dl_flags[1] == 'T' || dl_flags[2] == 'T')
  579.         dlt = 1;                        // unlimited download time flag
  580.       desc = next_word(desc);           // shift the description pointer
  581.       }
  582.     }
  583.  
  584.   low  = 0;                             // index of first entry
  585.   high = x-1;                           // index of last entry
  586.   while (low <= high) {                 // binary search in array
  587.     index = (high + low) / 2;           // middle of interval
  588.     if ((b = wild_comp(fa[index]->fname,filename)) < 0)
  589.       low = index + 1;                  // new low boundary
  590.     else if (b > 0)                     // 'b'-value from previous 'if'
  591.       high = index - 1;                 // new high boundary
  592.     else
  593.       break;                            // equal: found
  594.     }
  595.  
  596.   tp = NULL;                            // init to 'not offline'
  597.   if (x < 1  ||                         // no files in directory at all
  598.       wild_comp(fa[index]->fname,filename)) {  // this one not in dir
  599.     tp = file_element(parea, NULL);     // alloc new element
  600.     strcpy(tp->fname, filename);        // copy filename to new element
  601.     tp->fdesc = (desc != NULL) ? desc : NDS; // assign 'some' desc
  602.     tp->priv = fpriv;                   // copy privilege
  603.     tp->fseq = bbsseq;                  // copy FILES.BBS line number
  604.     tp->dl_b = dlb;                     // copy download bytes flag
  605.     tp->dl_t = dlt;                     // copy download time flag
  606.     if (oper_mode == VERBOSE) {
  607.       cprintf("\r     %c", 25);         // erase progress counter
  608.       printf("\n        %s  %s", OFFLINE, filename); // display filename
  609.       }
  610.     }
  611.   else {                                // file found in directory
  612.     n = index;                          // 'hit' and lower entries
  613.     while (n>=0 && wild_comp(fa[n]->fname,filename) == 0) {
  614.       if (fa[n]->fdesc == NULL)         // no description assigned yet
  615.         desc_old(fa, desc, parea, n, fpriv, bbsseq, dlb, dlt);
  616.       else if (strcmp(fa[n]->parea->name,parea->name))  // diff. dir
  617.         tp = desc_dup(fa[n], desc, parea, fpriv, bbsseq, dlb, dlt); // new
  618.       --n;                              // next lower
  619.       }
  620.     n = index + 1;                      // higher entries
  621.     while (n<x && wild_comp(fa[n]->fname,filename) == 0) {
  622.       if (fa[n]->fdesc == NULL)         // no description assigned yet
  623.         desc_old(fa, desc, parea, n, fpriv, bbsseq, dlb, dlt);
  624.       else if (strcmp(fa[n]->parea->name,parea->name))  // diff. dir
  625.         tp = desc_dup(fa[n], desc, parea, fpriv, bbsseq, dlb, dlt); // new
  626.       ++n;                              // next higher
  627.       }
  628.     }
  629.   return(tp);                           // return pointer to new(?)
  630.                                         // element or NULL if not new
  631.   }
  632.  
  633. /* ---------------------------------------------- */
  634. /* Create duplicate file element with description */
  635. /* ---------------------------------------------- */
  636. struct _filechain *desc_dup(struct _filechain *fa,
  637.                             char *desc,
  638.                             struct _downpath huge *parea,
  639.                             short int fpriv,
  640.                             unsigned short int bbsseq,
  641.                             unsigned short int dlb,
  642.                             unsigned short int dlt)
  643. {
  644.   struct _filechain *tp;                // ptr to file-info
  645.  
  646.   tp = file_element(parea, NULL);       // alloc new element
  647.   tp->wdate = fa->wdate;                // copy file wrtie date
  648.   tp->wtime = fa->wtime;                // copy file write time
  649.   tp->cdate = fa->cdate;                // copy file create date
  650.   tp->ctime = fa->ctime;                // copy file create time
  651.   tp->size  = fa->size;                 // copy file size
  652.   tp->attr  = fa->attr;                 // copy file attr
  653.   tp->fseq  = bbsseq;                   // copy FILES.BBS line number
  654.   tp->priv  = fpriv;                    // copy privilege
  655.   tp->dl_b  = dlb;                      // copy download bytes flag
  656.   tp->dl_t  = dlt;                      // copy download time flag
  657.   strcpy(tp->fname, fa->fname);         // copy filename to new element
  658.   tp->fdesc = (desc != NULL) ? desc : NDS;  // add pointer to desc.
  659.   return(tp);                           // return pointer
  660.   }
  661.  
  662. /* --------------------------------------------- */
  663. /* Update existing file element with description */
  664. /* --------------------------------------------- */
  665. void  desc_old(struct _filechain **fa,
  666.                char *desc,
  667.                struct _downpath huge *parea,
  668.                short int n,
  669.                short int fpriv,
  670.                unsigned short int bbsseq,
  671.                unsigned short int dlb,
  672.                unsigned short int dlt)
  673. {
  674.   fa[n]->fdesc = (desc != NULL) ? desc : NDS;  // ptr to descr.
  675.   fa[n]->parea = parea;                 // copy / overwrite area ptr
  676.   fa[n]->priv  = fpriv;                 // copy privilege
  677.   fa[n]->dl_b = dlb;                    // copy download bytes flag
  678.   fa[n]->dl_t = dlt;                    // copy download time flag
  679.   fa[n]->fseq  = bbsseq;                // copy FILES.BBS line number
  680.   }
  681.  
  682. /* ---------------------------------------- */
  683. /* Add single file-entry to file-chain      */
  684. /* returns: pointer to new element          */
  685. /* ---------------------------------------- */
  686. struct _filechain *file_element(struct _downpath huge *parea,
  687. #ifndef __32BIT__
  688.                                 struct _FILEFINDBUF *cf)
  689. #else
  690.                                 struct _FILEFINDBUF3 *cf)
  691. #endif
  692. {
  693.   struct _filechain *tp;                // temporary-pointer
  694.  
  695.   tp = (struct _filechain *)malloc(sizeof(struct _filechain));
  696.   if (tp == NULL) {
  697.      printf(MSG_MEM,PROGNAME);          // not enough memory
  698.      DosExit(11,0);
  699.      }
  700.   memset(tp, '\0', sizeof(struct _filechain)); // whole struct zeroes
  701.   tp->next_element = NULL;              // ptr to next
  702.   tp->fseq  = 65535;                    // default FILES.BBS line number
  703.   tp->priv  = HIDDEN;                   // unless FILES.BBS proves otherwise
  704.   tp->fdesc = NULL;                     // unless found in FILES.BBS
  705.   tp->parea = parea;                    // copy pointer to area info
  706.   if (cf != NULL) {                     // file-system info available
  707.     tp->wdate = cf->fdateLastWrite;
  708.     tp->wtime = cf->ftimeLastWrite;
  709.     if (file_time(cf->fdateCreation, cf->ftimeCreation) >
  710.         file_time(cf->fdateLastWrite, cf->ftimeLastWrite) ) {  /* HPFS */
  711.       tp->cdate = cf->fdateCreation;    // creation most recent
  712.       tp->ctime = cf->ftimeCreation;
  713.       }
  714.     else {                              // otherwise and non-HPFS volumes
  715.       tp->cdate = cf->fdateLastWrite;
  716.       tp->ctime = cf->ftimeLastWrite;
  717.       }
  718.     tp->size  = cf->cbFile;
  719.     tp->attr  = cf->attrFile;
  720.     strncpy(tp->fname, cf->achName, MAXFN);
  721.     }
  722.   return(tp);
  723.   }
  724.  
  725. /* --------------------------------- */
  726. /* Build comment entry in file-chain */
  727. /* --------------------------------- */
  728. struct _filechain *add_comment(char  *filename,
  729.                                struct _downpath huge *parea,
  730.                                char   *fdesc,
  731.                                short  int fpriv,
  732.                                unsigned short int bbsseq)
  733. {
  734.   struct _filechain *tp;                // ptr to file-info
  735.   char   *fd;                           // ptr to file description
  736.   char   buf2[MAXDESC];                 // work buffer
  737.   unsigned short int k;                 // counter
  738.  
  739. /* strcpy                               55h */
  740.   strncpy(buf2, fdesc, MAXDESC);        // save desc in buf2
  741.   for (k=0; buf2[k]!='\0'; ++k)         // scan work buffer
  742.     if (buf2[k]=='\r' || buf2[k]=='\n') { // for NL/CR
  743.       buf2[k] = '\0';                   // replace by end-of-string
  744.       break;
  745.       }
  746.   tp = file_element(parea, NULL);       // alloc new element
  747. /* strcpy                               55h */
  748.   strncpy(tp->fname, filename, MAXFN);  // EMPTY for comment
  749.   tp->priv = fpriv;                     // privilege
  750.   tp->fseq = bbsseq;                    // FILES.BBS line number
  751.   tp->cmt  = 1;                         // indicate as comment element
  752.   fd = malloc(k + 1);                   // obtain memory for desc
  753.   if (fd == NULL) {
  754.     printf(MSG_MEM,PROGNAME);           // not enough memory
  755.     DosExit(12,0);
  756.     }
  757.   else {
  758.     strcpy(fd, buf2);                   // store description
  759.     tp->fdesc = fd;                     // pointer to desc
  760.     }
  761.   return(tp);                           // return pointer
  762.   }
  763.  
  764. /* ----------------------------------------------------------- */
  765. /* Release memory occupied by orphan entries.                  */
  766. /* If first element contains an orphan it will not be removed! */
  767. /* ----------------------------------------------------------- */
  768. unsigned short int free_orphan()
  769. {
  770.   unsigned short int  orp_cnt;          // removed orphan counter
  771.   struct _filechain *ca,*cb;            // current and next chain pointer
  772.  
  773.   orp_cnt = 0;                          // initial orphan count
  774.   if ((ca=first_element) != NULL) {     // any file elements in chain
  775.     while ((cb=ca->next_element) != NULL) {  // all elements (but first)
  776.       if (cb->priv >= HIDDEN) {         // probably orphan?
  777.         ca->next_element = cb->next_element; // new ptr in current element
  778.         free(cb);                       // free memory block
  779.         ++orp_cnt;                      // one more dropped
  780.         }                               // note: do not shift current
  781.       else                              // not an orphan
  782.         ca = cb;                        // shift +1 current element
  783.       }
  784.     }                                   // only orphans
  785.   return(orp_cnt);                      // report # of removed orphans
  786.   }
  787.  
  788. /* ---------------------------------------------------- */
  789. /* Combine consecutive comment lines to a single string */
  790. /* return the number of freed chain elements            */
  791. /* ---------------------------------------------------- */
  792. unsigned short int combine_comments(void)
  793. {
  794.   unsigned short int  cmt_cnt;          // freed elements count
  795.   struct _filechain *ca,*cb;            // current and next chain pointer
  796.   char *new;                            // ptr to combined comments
  797.   unsigned int k,l;                     // string lengths
  798.  
  799. #if defined(DEBUG)
  800.   dump_file_chain(first_element, "begin combine_comments()");
  801. #endif
  802.   cmt_cnt = 0;                          // initial freed elements count
  803.   if ((ca=first_element) != NULL) {     // any elements in chain
  804.     while ((cb=ca->next_element) != NULL) {  // all elements
  805.       if (ca->fname[0] == '\0'  &&      // comment #1
  806.           cb->fname[0] == '\0'  &&      // comment #2
  807.           cb->fseq == ca->fseq+1) {     // consecutive lines in FILES.BBS
  808.         k = strlen(ca->fdesc);          // take length of 1st comment
  809.         l = strlen(cb->fdesc);          // take length of 2nd comment
  810.         if ((new = (char *)malloc(k+l+2)) != NULL) {  // mem obtained
  811.           strcpy(new,ca->fdesc);        // copy 1st comment
  812.           strcat(new,"\n");             // insert newline
  813.           strcat(new,cb->fdesc);        // copy 2nd comment
  814.           free(ca->fdesc);              // release 1st old comment
  815.           free(cb->fdesc);              // release 2nd old comment
  816.           ca->fdesc = new;              // ptr to combined desc in cur el.
  817.           ca->next_element = cb->next_element; // new ptr in cur el.
  818.           free(cb);                     // release 2nd chain-element
  819.           ++cmt_cnt;                    // update counter
  820.           }
  821.         else
  822.           ca = cb;                      // no memory: proceed
  823.         }
  824.       else                              // not 2 consecutive comments
  825.         ca = cb;                        // shift +1 current element
  826.       }
  827.     }                                   // only orphans
  828. #if defined(DEBUG)
  829.   dump_file_chain(first_element, "end combine_comments()");
  830. #endif
  831.   return(cmt_cnt);                      // report # of removed elements
  832.   }
  833.  
  834. /* ----------------------------------- */
  835. /* Check if string could be a filename */
  836. /* ----------------------------------- */
  837. char *parse_fname(char *buf)
  838. {
  839.   if ( strlen(buf) < 2  ||             // empty line
  840.        buf[0] <= ' '  ||
  841.        buf[0] == '\"' ||
  842.       (buf[0] >= '+'  && buf[0] <= '/')  ||
  843.       (buf[0] >= ':'  && buf[0] <= '>')  ||
  844.       (buf[0] >= '['  && buf[0] <= ']')  ||
  845.        buf[0] == '|'  ||
  846.        buf[0] >= 128)
  847.     return(NULL);                       // probably not a filename
  848.   else
  849.     return(buf);                        // pointer to filename
  850.   }
  851.  
  852.  
  853. /* ------------------------------------- */
  854. /* Determine if area is within selection */
  855. /* ------------------------------------- */
  856. int  area_selection(char *name,
  857.                     char sel_array[][MAXANAME])
  858. {
  859.   int   result;
  860.   short int i;
  861.  
  862.   if (area_IN_EX) {                     // area selection specified
  863.     result = (area_IN_EX < 0) ? 1 : 0;  // default not found: IN=0 EX=1
  864.     for (i=0; strcmp(sel_array[i], EMPTY); i++) {
  865.       if (!strnicmp(sel_array[i], name, strlen(sel_array[i]))) {
  866.         result = (area_IN_EX < 0) ? 0 : 1;  // found: IN=1 EX=0
  867.         break;                          // escape from while-loop
  868.         }
  869.       }
  870.     }
  871.   else
  872.     result = 1;                         // no selections: all
  873.   return(result);                       // return selection result
  874.   }
  875.  
  876. /* =================================================== */
  877. /* Compare for sort of area-array on download pathname */
  878. /* =================================================== */
  879. int  sort_path(const void *p,
  880.                const void *q)
  881. {
  882.   int  rc;
  883.   struct _downpath huge *x, *y;
  884.   x = (struct _downpath huge *)p;
  885.   y = (struct _downpath huge *)q;
  886.   if ((rc = stricmp(x->pname, y->pname)) == 0)  // download pathname
  887.     rc = stricmp(x->name, y->name);     // area name
  888.   return(rc);
  889.   }
  890.  
  891.  
  892. #if defined(DEBUG)
  893. /* =============== */
  894. /* Dump area array */
  895. /* =============== */
  896. int dump_area_array(struct _downpath huge *aa,  // pointer to array
  897.                     short int m,                // number of elements
  898.                     char *id)                   // log id
  899. {
  900.   short int i;
  901.   FILE *log;
  902.  
  903.   log = fopen("downsort.log","a");      // append!
  904.   fprintf(log,"\n");                    // newline
  905.   sep_line(log,'═',79);                 // separate from previous append
  906.   fprintf(log,"(%s) %s\n", today, id);  // timestamp + id
  907.   flushall();
  908.   for (i=0; i<m; i++) {
  909.     fprintf(log,"\n%4d. bytecount=%10lu priv=%5d anum=%d filecount=%5u\n",
  910.         i+1,
  911.         aa[i].byte_count,
  912.         aa[i].priv,
  913.         aa[i].anum,
  914.         aa[i].file_count);
  915.     fprintf(log," name %s\nename %s\npname %s\nf_bbs %s\n desc %s\n",
  916.         aa[i].name,
  917.         aa[i].ename,
  918.         aa[i].pname,
  919.         aa[i].filesbbs,
  920.         aa[i].adesc);
  921.     fprintf(log,"newst %s\n",
  922.         (aa[i].newest != NULL) ? aa[i].newest->fname : "-");
  923.     flushall();                         // write buffers before continue
  924.     }
  925.   fclose(log);
  926.   return(i);
  927.   }
  928.  
  929. /* =============== */
  930. /* Dump file chain */
  931. /* =============== */
  932. int dump_file_chain(struct _filechain *f,       // pointer to chain
  933.                     char *id)                   // log id
  934. {
  935.   FILE *log;
  936.   unsigned short int fc;                // element count
  937.   struct _filechain *fe;                //
  938.  
  939.   log = fopen("downsort.log","a");      // append!
  940.   fprintf(log,"\n");                    // newline
  941.   sep_line(log,'═',79,0);               // separate from previous append
  942.   fprintf(log,"(%s) %s\n\n", today, id);  // timestamp + id
  943.   flushall();                           // force physical write(s)
  944.   fe = f;                               // pointer to first element
  945.   fc = 0;                               // counter=0
  946.   while(fe != NULL) {
  947.     if (fe->fname[0] != '\0') {
  948.       fprintf(log,"%*.*s", MAXANAME, MAXANAME, fe->parea->name); // area
  949.       fprintf(log," %s ", fe->fname);   // filename
  950.       }
  951.     fprintf(log,"%s\n",
  952.                 (fe->fdesc != NULL) ? fe->fdesc : EMPTY);  // file desc
  953.     fc++;
  954.     fe = fe->next_element;
  955.     }
  956.   fprintf(log,"\nChain_element_count = %u\n\n", fc);
  957.   fclose(log);
  958.   return(fc);
  959.   }
  960. #endif
  961.  
  962.