home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / dwnsrs59.zip / DOWNCOL.C < prev    next >
C/C++ Source or Header  |  1993-12-19  |  56KB  |  1,153 lines

  1.  
  2. /* ============================================================ */
  3. /*  Rob Hamerling's MAXIMUS download file scan and sort utility. */
  4. /*  -> DOWNCOL.C                                                 */
  5. /*  -> Functions to collect download file information.           */
  6. /* ============================================================= */
  7.  
  8. /* #define __DEBUG__  */
  9.  
  10. #define INCL_DOS
  11. #define INCL_DOSFILEMGR
  12. #define INCL_DOSERRORS
  13. #define INCL_NOPMAPI
  14. #include <os2.h>
  15.  
  16. #include <conio.h>
  17. #include <fcntl.h>
  18. #include <io.h>
  19. #include <malloc.h>
  20. #include <share.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys\types.h>
  25.  
  26. #include "..\max\mstruct.h"             /* MAXIMUS definitions          */
  27. #include "downsort.h"                   /* downsort defines             */
  28. #include "downfpro.h"                   /* downsort function prototypes */
  29.  
  30.  
  31. /* prototypes of local functions */
  32.  
  33. FILECHAIN *add_comment(char *, DOWNPATH *,
  34.                        char *, short int, USHORT);
  35. void       add_pathspec(char *, FILECHAIN *);
  36. void       add_to_chain(FILECHAIN **, FILECHAIN *);
  37. int        area_selection(char *);
  38. FILECHAIN *assign_desc(long int, char *, FILECHAIN **,
  39.            DOWNPATH _HUGE *, char *, short int, USHORT);
  40. char      *build_desc(FILE *, char *, char **);
  41. long int   collect_area(DOWNPATH _HUGE **);
  42. long int   collect_file(USHORT, DOWNPATH _HUGE *);
  43. long int   combine_comments(void);
  44. long int   count_orphans(FILECHAIN *);
  45. FILECHAIN *desc_dup(FILECHAIN *, char *,
  46.                DOWNPATH _HUGE *, short int, USHORT, USHORT, USHORT);
  47. int        file_selection(char *);
  48. long int   fill_chn(DOWNPATH *, FILECHAIN **);
  49. #ifndef __32BIT__
  50.   FILECHAIN *file_element(DOWNPATH *, FILEFINDBUF *);
  51. #else
  52.   FILECHAIN *file_element(DOWNPATH *, FILEFINDBUF3 *);
  53. #endif
  54. long int   free_orphan(FILECHAIN **);
  55. long int   get_desc(DOWNPATH *, FILECHAIN **);
  56. char      *parse_fname(char *);
  57. int        sort_fcol(const void *, const void *);
  58. int        sort_path(const void *, const void *);
  59. unsigned int  split_fname(char *);
  60.  
  61. #ifdef __DEBUG__
  62.   USHORT  dump_area_array(DOWNPATH _HUGE *, ULONG, char *);
  63.   ULONG   dump_file_chain(FILECHAIN *, char *);
  64. #endif
  65.  
  66. /* -------------------------------------- */
  67. /* Collect download file area information */
  68. /* returns - number of area's             */
  69. /*         - pointer to (huge) array      */
  70. /* -------------------------------------- */
  71. long int collect_area(DOWNPATH _HUGE **area)
  72. {
  73. #ifndef __32BIT__
  74.   FILESTATUS  fs;                       /* file status buffer */
  75.   USHORT  af;                           /* file handle */
  76.   USHORT  oaction;                      /* open_action */
  77. #else
  78.   FILESTATUS3 fs;                       /* file status buffer */
  79.   ULONG   af;                           /* file handle */
  80.   ULONG   oaction;                      /* open_action */
  81. #endif
  82.   int     rc;                           /* returncode Dos-calls */
  83.   long int i,j,k;                       /* signed counter(s) */
  84.   ULONG   m;                            /* unsigned counter(s) */
  85.   struct _area a;                       /* MAXIMUS (minimum) area struct.*/
  86.   DOWNPATH _HUGE *d;                    /* ptr to array with area-info   */
  87.  
  88. #ifdef __DEBUG__
  89.   remove("downsort.log");               /* remove previous debug file */
  90. #endif
  91.   af = 0;                               /*
  92.   oaction = 0;                          /* init */
  93.   rc = DosOpen(areadat_path,            /* open 'carefully' */
  94.           &af,                          /* pointer to File Handle */
  95.           &oaction,                     /* pointer to action field */
  96.           0L,                           /* new filesize (N/A) */
  97.           FILE_NORMAL,                  /* attributes */
  98.           FILE_OPEN,                    /* open flags */
  99.           OPEN_ACCESS_READONLY    +     /* mode */
  100.             OPEN_SHARE_DENYWRITE,
  101.           0L);                          /* no EA's */
  102.   if (rc != NO_ERROR) {                 /* open error */
  103.     fprintf(stderr, MSG_OPI, areadat_path, rc);
  104.     exit(rc);
  105.     }
  106.   if (oaction != FILE_EXISTED) {        /* not found */
  107.     fprintf(stderr, MSG_OPA, areadat_path, oaction);
  108.     exit(5);
  109.     }
  110.   else {
  111.     if (oper_mode != QUIET)
  112.       fprintf(stdout, MSG_COA, areadat_path);
  113. #ifndef __32BIT__
  114.     rc = DosQFileInfo(af, 1, &fs, sizeof(fs));     /* info AREA.DAT */
  115. #else
  116.     rc = DosQueryFileInfo(af, 1, &fs, sizeof(fs)); /* info AREA.DAT */
  117. #endif
  118.     read(af, (char *)&a, sizeof(struct _area));  /* obtain first part */
  119.     m = 0;                              /* init participating file count */
  120.     if (a.id != AREA_id) {              /* validate MAXIMUS format */
  121.       fprintf(stderr, MSG_MX1, areadat_path);
  122.       fprintf(stderr, MSG_MX2, PROGNAME,VERSION,SUBVERS,SUFFIX,MAX);
  123.       }
  124.     else {                              /* acceptable AREA.DAT */
  125.       k = (ushort)(fs.cbFile / a.struct_len);     /* # area's */
  126.       for (i=0; i<k; i++) {             /* count area's with downloads */
  127.         lseek(af, i*(long)a.struct_len, SEEK_SET);  /* locate to next */
  128.         read(af, (char *)&a, sizeof(struct _area));
  129.         if (a.filepath[0] != '\0'       &&
  130.             a.filepath[0] != ' '        &&
  131.             a.filepriv <= ABS_MAX_priv  &&   /* within report privilege */
  132.             (areakeys ^ (a.filelock | areakeys)) == 0  &&   /* all keys */
  133.             area_selection(a.name))     /* check include and exclude */
  134.           m++;                          /* participating filearea count */
  135.         }
  136.       if (m > 0) {                      /* any area's participating */
  137. #ifndef __32BIT__
  138.         d = (DOWNPATH huge *)halloc(m, sizeof(DOWNPATH));
  139. #else
  140.         d = (DOWNPATH *)malloc(m * sizeof(DOWNPATH));
  141. #endif
  142.         if (d == NULL) {                /* memory not obtained? */
  143.           fprintf(stderr, MSG_MEM, PROGNAME);
  144.           exit(6);
  145.           }
  146.         else {                          /* have memory for area-info */
  147.           *area = d;                    /* return pointer to caller */
  148.           for (i=0, m=0; i<k; i++) {    /* all area's */
  149.             lseek(af, i*(long)a.struct_len, SEEK_SET);  /* locate to next*/
  150.             read(af, (char *)&a, sizeof(struct _area));
  151.             if ( a.filepath[0] != '\0'      &&  /* filename(?) */
  152.                  a.filepath[0] != ' '       &&  /* filename(?) */
  153.                  a.filepriv <= ABS_MAX_priv &&  /* within report privileg*/
  154.                 (areakeys ^ (a.filelock | areakeys)) == 0  &&    /* keys */
  155.                  area_selection(a.name)) {      /* check selection */
  156.               d[m].priv = a.filepriv;           /* download privilege */
  157.               d[m].anum = (short)i;             /* AREA.DAT seq. nbr     */
  158.               d[m].filelock = a.filelock;       /* File Lock             */
  159.               d[m].file_count = 0;              /* init file count       */
  160.               d[m].byte_count = 0L;             /* init byte count       */
  161.               d[m].newest     = NULL;           /* pointer to newest file*/
  162.               strcpy(d[m].name, a.name);        /* area name ... edit!   */
  163.               strcpy(d[m].pname, a.filepath);   /* download path         */
  164.               j = strlen(d[m].pname);
  165.               if (j>0 && d[m].pname[j-1] != '\\')
  166.                 d[m].pname[j] = '\\';           /* backslash if needed   */
  167.               strcpy(d[m].filesbbs, a.filesbbs); /* files.bbs */
  168.               strcpy(d[m].adesc, a.fileinfo);   /* filearea title */
  169.               ++m;                              /* filearea index */
  170.               }
  171.             }
  172.           }
  173.                             /* prepare here area-names for group sorting */
  174.         max_aname = 0;                  /* init max areaname length */
  175.         for (i=0; i<m; i++)             /* all collected area's */
  176.           max_aname = (unsigned short)max(strlen(d[i].name), max_aname);
  177.         for (i=0; i<m; i++) {           /* all collected area's */
  178.           sprintf(d[i].ename,
  179.                  (d[i].name[0]<'0' || d[i].name[0]>'9')  /* not numeric */
  180.                     ? "%-*.*s" : "%*.*s",  /* left or right aligned */
  181.                   max_aname, max_aname, d[i].name);
  182.           d[i].ename[max_aname] = '\0'; /* end of string */
  183.           j = k = max_aname-1;          /* init offset values */
  184.           while (j>0 && d[i].ename[j] == ' ')   /* search last char */
  185.             j--;
  186.           if (j < k) {                  /* spaces found */
  187.             while (j>0                &&
  188.                    d[i].ename[j]>='0' &&
  189.                    d[i].ename[j]<='9') {
  190.               d[i].ename[k--] = d[i].ename[j];  /* move */
  191.               d[i].ename[j--] = ' ';            /* replace by blank */
  192.               }
  193.             }
  194.           }
  195.         }
  196.       }
  197. #if defined(__DEBUG__)
  198.       dump_area_array(*area, m, "after area.dat processing");
  199. #endif
  200.     DosClose(af);                       /* close area.dat-file */
  201.     fflush(stdout);                     /* secure information messages */
  202.     fflush(stderr);                     /* secure error messages */
  203.     return((int) m);                    /* report number downloadarea's */
  204.     }
  205.   return(0);
  206.   }
  207.  
  208. /* ---------------------------- */
  209. /* Collect all file information */
  210. /* ---------------------------- */
  211. long int collect_file(USHORT   a,       /* number entries in area-array */
  212.                       DOWNPATH _HUGE *area)  /* pointer to area array */
  213. {
  214.   long int  i, k, l;                    /* counter(s) */
  215.   long int  file_count;                 /* total file count */
  216.   long int  orpcnt;                     /* total orphan count */
  217.   FILECHAIN *ca,                        /* ptr to first of path */
  218.             *ce;                        /* ptr to file info */
  219.  
  220.   file_count = 0;                       /* init */
  221.   orpcnt = 0;                           /* init */
  222.   if (oper_mode != QUIET)
  223.     fprintf(stdout, MSG_COF);
  224.   qsort(area, a, sizeof(DOWNPATH), sort_path);
  225.                          /* WARNING: do no sort this array ever again! */
  226.   ca = NULL;                            /* init: no group yet */
  227.   for (i=0; i<a; i++) {                 /* all 'included' download areas */
  228.     area[i].file_count = k = 0;         /* init filecount this area */
  229.     if (oper_mode == VERBOSE)
  230.       fprintf(stdout, MSG_ARE, k, area[i].pname, area[i].name);
  231.     else if (oper_mode != QUIET)
  232.       fprintf(stdout, DOT);
  233.     fflush(stdout);                     /* show output */
  234.     if (i<1 || stricmp(area[i].pname, area[i-1].pname)) {  /* pathnames */
  235.       if (lp[P_ORP].next==NULL)         /* orphan report not req'd */
  236.         orpcnt += free_orphan(&first_element); /* free rest-orphans */
  237.       k = fill_chn(&area[i], &ce);      /* read directory */
  238.                                         /* returns # of files and */
  239.                                         /*   pointer to first of group */
  240.       ca = NULL;                        /* indicate new group */
  241.       add_to_chain(&ca, ce);            /* add group(!) to chain */
  242.       }
  243.     else                                /* new area, same DL-dir */
  244.       k = 0;                            /* restart directory file count */
  245.     if (oper_mode==VERBOSE)
  246.       fprintf(stdout, "%6ld\n", k);     /* total files this DL-dir */
  247.     l = get_desc(&area[i], &ca);        /* obtain descriptions */
  248.     if (oper_mode==VERBOSE && l>0)      /* offline files added to chain */
  249.       fprintf(stdout, MSG_ARD, k+l, l, area[i].name);
  250.     file_count += k + l;                /* add to # of input entries */
  251.     }                                   /* all area's processed */
  252.  
  253.   if (lp[P_ORP].next==NULL)             /* orphan report not req'd */
  254.     orpcnt += free_orphan(&first_element);  /* free orphans */
  255.   if (oper_mode==VERBOSE && orpcnt>0)
  256.     fprintf(stdout, MSG_ORP, orpcnt);
  257.   else if (oper_mode!=QUIET)
  258.     fprintf(stdout,"\n");               /* newline..flush */
  259.  
  260.   file_count -= orpcnt;                 /* less dropped orphans */
  261.  
  262.   if (lp[P_FIL].next != NULL) {         /* FILES.BBS report requested */
  263.     ce = first_element;                 /* head of chain */
  264.     while (ce != NULL) {                /* whole chain */
  265.       ce->parea->file_count++;          /* bump up area filecount */
  266.       ce = ce->next_element;            /* ptr to next element */
  267.       }
  268.     ca = NULL;                          /* new 'group' */
  269.     for (i=0; i<a; i++) {               /* all area's in array */
  270.       if (area[i].file_count == 0) {    /* no files in this area */
  271.         ce = add_comment(EMPTY, &area[i],     /* dummy filename */
  272.                          " ", TWIT, 0xFFFF);  /* 'dummy' decription */
  273.         add_to_chain(&ca, ce);          /* add element to chain */
  274.         file_count++;                   /* update total count */
  275.         if (oper_mode==VERBOSE)
  276.           fprintf(stdout, MSG_ARC, area[i].name);  /* added placeholder */
  277.         }
  278.       }
  279.     }
  280.  
  281.   if (file_count) {                     /* file(s) present */
  282.     if ((ca = first_element) != NULL) { /* pointer to first element */
  283.       for (k=1; ca->next_element != NULL; k++)   /* at least 1, find last */
  284.         ca = ca->next_element;          /* pointer to next */
  285.       }
  286.     else
  287.       k=0;                              /* no files in chain */
  288.     if (file_count != k) {              /* compare counts */
  289.       DosBeep(880,50);
  290.       DosBeep(440,50);                  /* warning signal */
  291.       DosBeep(880,50);
  292.       fprintf(stderr, MSG_IEC, PROGNAME, file_count, k);  /* internal error */
  293. #if defined(__DEBUG__)
  294.       dump_file_chain(first_element, "file_count / chain discrepancy");
  295. #endif
  296.       exit(99);                         /* quit */
  297.       }
  298.     }
  299.   return(file_count);                   /* file total count */
  300.   }
  301.  
  302. /* ---------------------------------------- */
  303. /* Add all subdir-filenames to the chain,   */
  304. /* for further processing by mainline.      */
  305. /* NOTE: - full path name assumed!          */
  306. /* returns - number of files in this area   */
  307. /*         - pointer to first element       */
  308. /* ---------------------------------------- */
  309. long int fill_chn(DOWNPATH *parea,      /* pointer to area array */
  310.                   FILECHAIN **cp)       /* return-ptr to first element */
  311. {
  312.   int      rc;                          /* returncode of DOS-calls */
  313.   long int fc;                          /* area-file counter */
  314.   FILECHAIN  *ce, *tp;                  /* curr, temp chain ptrs */
  315.   char   down_spec[MAXPATH];            /* file specification buffer */
  316.   HDIR   fhandle;                       /* FindFirst/Last handle */
  317.  
  318. #ifndef __32BIT__
  319. #define  MAXENT 1                       /* must be 1 for DOS */
  320.   FILEFINDBUF buf;                      /* file-info buffer (1 entry!) */
  321.   FILEFINDBUF *cf;                      /* pointer to current entry */
  322.   USHORT fentries;                      /* # entries to be retrieved */
  323. #else
  324. #define  MAXENT 32                      /* dir-entries per DosFind */
  325.   int    i;                             /* file-entry counter */
  326.   char   buf[MAXENT * sizeof(FILEFINDBUF3)];    /* file-info buffer */
  327.   FILEFINDBUF3  *cf;                    /* pointer to current entry */
  328.   ULONG  fentries;                      /* # entries to be retrieved */
  329. #endif
  330.   char  *pBuf;                          /* buffer pointer */
  331.  
  332.   fc = 0;                               /* init filecount this dir */
  333.   *cp = NULL;                           /* indicate in group yet */
  334.   strcpy(down_spec,parea->pname);       /* path */
  335.   strcat(down_spec, "*.*");             /* file-spec */
  336.   fentries = MAXENT;                    /* # to retrieve at a time */
  337.   fhandle  = HDIR_CREATE;               /* FindFirst/Last handle */
  338.   rc = DosFindFirst(down_spec,
  339.                     &fhandle,
  340.                     FILE_NORMAL,
  341.                     &buf,
  342.                     sizeof(buf),
  343.                     &fentries,
  344. #ifndef __32BIT__
  345.                     0L);                /* unused with OS/2 < 2.0 */
  346. #else
  347.                     FILE_ARCHIVED || FILE_READONLY); /* OS/2 2.0+ */
  348. #endif
  349.   while (rc == 0) {                     /* until no more files in dir */
  350.     pBuf = (char *)&buf;                /* init pointer to buffer */
  351. #ifndef __32BIT__
  352.     cf = (FILEFINDBUF *)pBuf;           /* pointer to first (only) entry */
  353. #else
  354.     cf = (FILEFINDBUF3 *)pBuf;          /* pointer to first (only) entry */
  355.     for (i=1; i<=fentries; i++) {       /* break after last entry! */
  356. #endif
  357.       if (file_selection(cf->achName)) {  /* not an excluded file */
  358.         tp = file_element(parea, cf);   /* add new element to chain */
  359.                                         /* note: exits if no memory */
  360.         if (*cp == NULL)                /* it is first element in group */
  361.           *cp = ce = tp;                /* return ptr, 1st and current */
  362.         else {                          /* not first */
  363.           ce->next_element = tp;        /* chain-ptr in previous */
  364.           ce = tp;                      /* new last in group */
  365.           }
  366.         ++fc;                           /* update filecount this area */
  367.         byte_count += cf->cbFile;       /* update file bytecount */
  368.  
  369.         if (oper_mode==VERBOSE && (fc%25)==0) {  /* every 25 files */
  370.           fprintf(stdout, "%6lu\r", fc);
  371.           fflush(stdout);
  372.           }
  373.         }
  374. #ifndef __32BIT__
  375.                                         /* nothing for OS/2 < 2.0 */
  376. #else
  377.       pBuf += cf->oNextEntryOffset;     /* set pointer to next entry */
  378.       cf = (FILEFINDBUF3 *)pBuf;        /* casted pointer */
  379.       }
  380. #endif
  381.     fentries = MAXENT;                  /* # to retrieve at a time */
  382.     rc = DosFindNext(fhandle,
  383.                      &buf,
  384.                      sizeof(buf),
  385.                      &fentries);
  386.     }
  387.   DosFindClose(fhandle);                /* close directory association */
  388.   return(fc);                           /* # of directory entries */
  389.   }
  390.  
  391. /* --------------------------------------------------- */
  392. /* Add file description to chain elements              */
  393. /* returns - number of additional entries in FILES.BBS */
  394. /*         - pointer to first file info structure      */
  395. /* --------------------------------------------------- */
  396. long int get_desc(DOWNPATH  *parea,     /* pointer to info of THIS area */
  397.                  FILECHAIN **cp)        /* return pointer 1st element */
  398. {
  399. #ifndef __32BIT__
  400.   USHORT  oaction;                      /* open action flags */
  401.   USHORT  fih;                          /* file handle */
  402. #else
  403.   ULONG   oaction;                      /* open action flags */
  404.   ULONG   fih;                          /* file handle */
  405. #endif
  406.   FILE   *fi;                           /* file pointer */
  407.   long int x, y;                        /* number of offline files */
  408.   FILECHAIN *ce, *tp;                   /* ptrs to file-info */
  409.   char   buf[MAXRCD];                   /* read-buffer for FILES.BBS */
  410.   char   filename[MAXFN];               /* filename from FILES.BBS buf */
  411.   char   *desc, *rp;                    /* ptr to desc / return string */
  412.   int    i;                             /* counter(s) */
  413.   short  int  k;                        /* counter(s) */
  414.   int    m;                             /* counter(s) */
  415.   short int fpriv;                      /* file privilege */
  416.   ULONG  rc;                            /* returncode */
  417.   int    fn_off;                        /* filename offset */
  418.   USHORT bbsseq;                        /* current line number FILES.BBS */
  419.   char   desc_spec[MAXPATH];            /* explicit pathspec FILES.BBS */
  420.   char   fpathspec[MAXPATH];            /* pathspec of file in FILES.BBS */
  421.   FILECHAIN **fa;                       /* pointer to file sort-array */
  422.  
  423.   if (strlen(parea->filesbbs) > 0)      /* explicit specification? */
  424.     strcpy(desc_spec,parea->filesbbs);  /* explicitly specified FileList */
  425.   else {                                /* no */
  426.     strcpy(desc_spec,parea->pname);     /* get path to download directory*/
  427.     strcat(desc_spec,lp[P_FIL].name);   /* add filename */
  428.     strcat(desc_spec,".");              /* add separator */
  429.     strcat(desc_spec,lp[P_FIL].ext);    /* add extension */
  430.     }
  431.  
  432.   fpriv = parea->priv;                  /* area-priv is default file priv*/
  433.                                         /* FILES.BBS may be in use!!!    */
  434.   fih = 0;                              /* file handle */
  435.   rc = DosOpen(desc_spec,               /* Open 'carefully' */
  436.           &fih,                         /* pointer to File Handle */
  437.           &oaction,                     /* pointer to action field */
  438.           0L,                           /* new filesize (N/A) */
  439.           FILE_NORMAL,                  /* attributes */
  440.           FILE_OPEN,                    /* open flags */
  441.           OPEN_ACCESS_READONLY    +     /* mode */
  442.             OPEN_SHARE_DENYWRITE,
  443.           0L);                          /* no EA's OS/2 2.0+ */
  444.   if (rc) {                             /* open error */
  445.     fprintf(stderr, MSG_OPI, desc_spec, rc);
  446.     return(0);                          /* no: return to caller */
  447.     }
  448.   if ((fi = fdopen(fih, "r")) == NULL) {
  449.     fprintf(stderr, MSG_OPA, desc_spec, -1);   /* dummy rc */
  450.     return(0);                          /* no: return to caller */
  451.     }
  452.  
  453.   ce = *cp;                             /* ptr to 1st element of group */
  454.   for (x=0; ce != NULL; x++)            /* count collected dir entries */
  455.     ce = ce->next_element;              /* pointer to next file entry */
  456.  
  457.   if (x > 0) {                          /* any files in directory */
  458.     fa = (FILECHAIN **)malloc((unsigned int)x * sizeof(FILECHAIN *));
  459.     if (fa == NULL) {                   /* not enough memory */
  460.       fprintf(stderr, MSG_MEM, PROGNAME);
  461.       exit(11);
  462.       }
  463.     ce = *cp;                           /* ptr to 1st element of group */
  464.     for (i=0; ce != NULL; i++) {        /* init sort array */
  465.       fa[i] = ce;                       /* element pointer into array */
  466.       ce = ce->next_element;            /* pointer to next file entry */
  467.       }
  468.     qsort(fa, (unsigned int)x, sizeof(FILECHAIN *), sort_fcol);
  469.     }
  470.   else                                  /* no files in group */
  471.     fa = NULL;                          /* no array allocated */
  472.  
  473.   y = 0;                                /* init 'offline' counter */
  474.   bbsseq = 0;                           /* init counter */
  475.   rp = fgets(buf, MAXRCD, fi);          /* get first FILES.BBS record */
  476.   while (rp != NULL) {                  /* until end of FILES.BBS */
  477.     bbsseq++;                           /* line counter */
  478.     m = strlen(buf);                    /* length of input string */
  479.     if (m>1 && buf[0] == '\20') {       /* privilege change (^P) ? */
  480.       for (k=0; k<HIDDEN-TWIT && priv_name[k][0]!=buf[1]; k++)
  481.         ;                               /* just scan */
  482.       if (TWIT + k > parea->priv)       /* only if higher than area-priv */
  483.         fpriv = k + TWIT;               /* new (higher) prilivege */
  484.       rp = fgets(buf, MAXRCD, fi);      /* continue */
  485.       }
  486.     else if (parse_fname(buf) == NULL) { /* NOT likely a filename */
  487.       if ( strncmp(buf, FILPREFX, 3)==0  ||  /* 3 chars only: bwd compat.*/
  488.            bbsseq <= 8 ) {              /* first 8 lines */
  489.         }                               /* just skip these */
  490.       else if (keepseq == KEEPSEQ) {    /* comments to be collected */
  491.         tp = add_comment(EMPTY, parea,     /* filename = null-string */
  492.                      buf, fpriv, bbsseq);
  493.         add_to_chain(cp, tp);           /* add element to end-of-chain */
  494.         ++y;                            /* add 1 to 'offline' filecount */
  495.         }
  496.       rp = fgets(buf, MAXRCD, fi);      /* continue with next line */
  497.       }
  498.     else {                              /* probably file descr record */
  499.       bbsseq = max(bbsseq, 8);          /* forced acceptance of comments */
  500.       for (i=0;                         /* starting point of scan */
  501.                 i < m               &&  /* string length */
  502.                 buf[i] != ' '       &&  /* space character */
  503.                 buf[i] != '\r'      &&  /* CR character */
  504.                 buf[i] != '\n';         /* LF character */
  505.                    i++)
  506.           ;                             /* scan for filespec */
  507.       strncpy(fpathspec, buf, i);       /* copy full filespec */
  508.       fpathspec[i] = '\0';              /* end of string */
  509.       strupr(fpathspec);                /* in capitals */
  510.       fn_off = split_fname(fpathspec);  /* split path */
  511.       strncpy(filename, fpathspec+fn_off, MAXFN);   /* copy filename */
  512.       filename[MAXFN-1] = '\0';         /* ensure end of string */
  513.       strupr(filename);                 /* filename in capitals */
  514.       if (file_selection(filename)) {   /* not excluded filename */
  515.         desc = build_desc(fi, buf, &rp);  /* build description string */
  516.         tp = assign_desc(x, filename,   /* assign desc to file(s) */
  517.                          fa, parea, desc, fpriv, bbsseq);
  518.         if (tp != NULL) {               /* new element for group */
  519.           add_to_chain(cp, tp);         /* add element to end-of-chain */
  520.           if (fn_off > 0)               /* pathspec -> extra file */
  521.             add_pathspec(fpathspec, tp);  /* add filesize/date, etc */
  522.           ++y;                          /* add 1 to 'extra' filecount */
  523.           }
  524.         }
  525.       else
  526.         rp = fgets(buf, MAXRCD, fi);    /* continue with next line */
  527.       }
  528.     }
  529.   if (fa != NULL)                       /* array memory was allocated */
  530.     free(fa);                           /* free temp sort array */
  531.   x = combine_comments();               /* combine consecutive comments */
  532.   fclose(fi);                           /* finished with this FILES.BBS */
  533.   DosClose(fih);                        /* close the handle */
  534.   return(y-x);                          /* return 'offline' filecount */
  535.                                         /* (includes comment elements) */
  536.   }
  537.  
  538. /* ---------------------------------- */
  539. /* Add a file element to end-of-chain */
  540. /* ---------------------------------- */
  541. void add_to_chain(FILECHAIN **cp,       /* ptr to ptr to a chain element */
  542.                   FILECHAIN *tp)        /* pointer to new element */
  543. {
  544.   FILECHAIN *ce;                        /* work pointer */
  545.  
  546.   if (tp == NULL)                       /* trying to add null element */
  547.     return;                             /* nothing to do here! */
  548.   if (*cp == NULL) {                    /* first / empty group (?) */
  549.     *cp = tp;                           /* return ptr to first in group */
  550.     if (first_element == NULL) {        /* whole chain empty */
  551.       first_element = tp;               /* very first: start of chain */
  552.       return;                           /* ===> nothing else to do here! */
  553.       }
  554.     else                                /* something in chain */
  555.       ce = first_element;               /* ptr to very 1st element */
  556.     }
  557.   else
  558.     ce = *cp;                           /* start search for last */
  559.  
  560.   while (ce->next_element != NULL)      /* search last element */
  561.     ce = ce->next_element;
  562.   ce->next_element = tp;                /* add new element to chain */
  563.   }
  564.  
  565. /* ------------------------------------- */
  566. /* Build file description memory block   */
  567. /* returns pointer to description string */
  568. /* ------------------------------------- */
  569. char *build_desc(FILE  *fi,             /* opened FILES.BBS */
  570.                  char  *ibuf,           /* file I/O read buffer */
  571.                  char  **rp)            /* pointer to next input buffer */
  572.                                         /*     or NULL if no more input */
  573. {
  574.   char   *fd;                           /* pointer memory block with desc*/
  575.   char   *desc;                         /* string work pointers */
  576.   char   buftmp[MAXDESC];               /* work buffer */
  577.   ULONG  k;                             /* counter */
  578.  
  579.   desc = next_word(ibuf);               /* locate description */
  580.   if (desc != NULL) {                   /* start of description found */
  581.     for (k=0; desc[k]; ++k) {           /* search first CR/LF */
  582.       if (desc[k]=='\r' || desc[k]=='\n') {
  583.         desc[k] = '\0';
  584.         break;
  585.         }
  586.       }
  587.     strncpy(buftmp, desc, MAXDESC-1);   /* save desc in buftmp */
  588.     while (((*rp = fgets(ibuf, MAXDESC, fi)) != NULL) &&  /* next rcd */
  589.            (ibuf[0] == ' ') &&          /* probably desc continuation */
  590.            ((desc = next_word(ibuf)) != NULL)) {
  591.       for (k=0; desc[k]!='\0'; ++k) {
  592.         if (desc[k]=='\r' || desc[k]=='\n') {
  593.           desc[k] = '\0';
  594.           break;
  595.           }
  596.         }
  597.       if ((strlen(buftmp) + k + 1) <= sizeof(buftmp)) {  /* not too long */
  598.         strcat(buftmp," ");             /* single space */
  599.         strcat(buftmp, desc);           /* concat desc to buftmp */
  600.         }
  601.       }
  602.     fd = malloc(strlen(buftmp)+1);      /* obtain memory */
  603.     if (fd == NULL) {
  604.       fprintf(stderr, MSG_MEM, PROGNAME);  /* not enough memory */
  605.       exit(12);
  606.       }
  607.     else
  608.       strcpy(fd, buftmp);               /* store description */
  609.     }
  610.   else {                                /* no description */
  611.     *rp = fgets(ibuf, MAXDESC, fi);     /* read ahead for next file */
  612.     fd = NULL;                          /* return value */
  613.     }
  614.   return(fd);                           /* return with pointer */
  615.   }
  616.  
  617. /* ------------------------------------- */
  618. /* Assign description to file element(s) */
  619. /* ------------------------------------- */
  620. FILECHAIN *assign_desc(long int x,            /* # of files in dir */
  621.                        char   *filename,      /* filespec */
  622.                        FILECHAIN **fa,        /* pointer to file ptr */
  623.                                               /* array of this area only */
  624.                        DOWNPATH _HUGE *parea, /* pointer to area */
  625.                        char   *desc,          /* description string */
  626.                        short  int fpriv,      /* file privilege */
  627.                        USHORT bbsseq)         /* seq# in FILES.BBS */
  628.                                               /* (partly for new files)  */
  629. {
  630.   long  int    low,high,index;          /* indexes */
  631.   long  int    b,n;                     /* counters */
  632.   USHORT       dlb,dlt;                 /* download flags in description */
  633.   FILECHAIN   *tp;                      /* ptr to file-info */
  634.   char         dl_flags[5];             /* copy of first part description*/
  635.  
  636.   dlb = dlt = 0;                        /* flags off */
  637.   while (desc != NULL) {                /* only when real descr. present */
  638.     strncpy(dl_flags, desc, 4);         /* copy part of description */
  639.     if (dl_flags[0] == '/') {           /* could be valid dl_flag */
  640.       strupr(dl_flags);                 /* make all uppercase */
  641.       if (dl_flags[1] == 'B' || dl_flags[2] == 'B')
  642.         dlb = 1;                        /* unlimited download bytes flag */
  643.       if (dl_flags[1] == 'T' || dl_flags[2] == 'T')
  644.         dlt = 1;                        /* unlimited download time flag  */
  645.       desc = next_word(desc);           /* shift the description pointer */
  646.       }
  647.     else                                /* not MAXIMUS dl-flag */
  648.       break;                            /* escape from while-loop */
  649.     }
  650.  
  651.   low  = 0;                             /* index of first entry */
  652.   high = x-1;                           /* index of last entry */
  653.   while (low <= high) {                 /* binary search in array */
  654.     index = (high + low) / 2;           /* middle of interval */
  655.     if ((b = wild_comp(fa[index]->fname, filename)) < 0)
  656.       low = index + 1;                  /* new low boundary  */
  657.     else if (b > 0)                     /* 'b'-value from previous 'if'  */
  658.       high = index - 1;                 /* new high boundary */
  659.     else
  660.       break;                            /* equal: found */
  661.     }
  662.  
  663.   tp = NULL;                            /* init to 'not offline' */
  664.   if (x < 1  ||                         /* no files in directory at all  */
  665.       wild_comp(fa[index]->fname,filename)) {  /* this one not in dir */
  666.     if (oper_mode == VERBOSE)
  667.       fprintf(stdout," \t%s  %s\n", OFFLINE, filename); /* filename */
  668.     tp = file_element(parea, NULL);     /* alloc new element */
  669.     strcpy(tp->fname, filename);        /* filename */
  670.     tp->fdesc = (desc != NULL) ? desc : NDS; /* assign 'some' desc */
  671.     tp->priv = fpriv;                   /* copy privilege */
  672.     tp->fseq = bbsseq;                  /* copy FILES.BBS line number */
  673.     tp->dl_b = dlb;                     /* copy download bytes flag */
  674.     tp->dl_t = dlt;                     /* copy download time flag */
  675.     }
  676.   else {                                /* file found in directory */
  677.     n = index;                          /* 'hit' and lower entries */
  678.     while (n>=0 && wild_comp(fa[n]->fname,filename) == 0) {
  679.       if (fa[n]->fdesc == NULL) {       /* no description assigned yet */
  680.         fa[n]->fdesc = (desc==NULL) ? NDS : desc;
  681.         fa[n]->parea = parea;           /* copy / overwrite area ptr */
  682.         fa[n]->priv  = fpriv;           /* copy privilege */
  683.         fa[n]->dl_b  = dlb;             /* copy download bytes flag */
  684.         fa[n]->dl_t  = dlt;             /* copy download time flag */
  685.         fa[n]->fseq  = bbsseq;          /* copy FILES.BBS line number */
  686.         }
  687.       else if (strcmp(fa[n]->parea->name,parea->name))  /* diff. dir */
  688.         tp = desc_dup(fa[n], desc, parea, fpriv, bbsseq, dlb, dlt); /*new*/
  689.       --n;                              /* next lower */
  690.       }
  691.     n = index + 1;                      /* higher entries */
  692.     while (n<x && wild_comp(fa[n]->fname,filename) == 0) {
  693.       if (fa[n]->fdesc == NULL) {       /* no description assigned yet   */
  694.         fa[n]->fdesc = (desc==NULL) ? NDS : desc;
  695.         fa[n]->parea = parea;           /* copy / overwrite area ptr */
  696.         fa[n]->priv  = fpriv;           /* copy privilege */
  697.         fa[n]->dl_b  = dlb;             /* copy download bytes flag */
  698.         fa[n]->dl_t  = dlt;             /* copy download time flag  */
  699.         fa[n]->fseq  = bbsseq;          /* copy FILES.BBS line number */
  700.         }
  701.       else if (strcmp(fa[n]->parea->name,parea->name))  /* diff. dir */
  702.         tp = desc_dup(fa[n], desc, parea, fpriv, bbsseq, dlb, dlt); /*new*/
  703.       ++n;                              /* next higher */
  704.       }
  705.     }
  706.   return(tp);                           /* return pointer to new(?)      */
  707.                                         /* element or NULL if not new    */
  708.   }
  709.  
  710. /* ---------------------------------------------------- */
  711. /* Create duplicate file element with description       */
  712. /* returns - pointer to new element (or NULL if no new) */
  713. /* ---------------------------------------------------- */
  714. FILECHAIN *desc_dup(FILECHAIN *fa,      /* pointer to file el. in array */
  715.                     char      *desc,    /* pointer to file desription */
  716.                     DOWNPATH _HUGE *parea,  /* pointer to area array */
  717.                     short int fpriv,    /* file privilege */
  718.                     USHORT    bbsseq,   /* seq# in FILES.BBS */
  719.                     USHORT    dlb,      /* download byte flag */
  720.                     USHORT    dlt)      /* download time flag */
  721. {
  722.   FILECHAIN *tp;                        /* ptr to file-info */
  723.  
  724.   tp = file_element(parea, NULL);       /* alloc new element  */
  725.   tp->wd.date = fa->wd.date;            /* copy file wrtie date */
  726.   tp->wt.time = fa->wt.time;            /* copy file write time */
  727.   tp->cd.date = fa->cd.date;            /* copy file create date */
  728.   tp->ct.time = fa->ct.time;            /* copy file create time */
  729.   tp->size  = fa->size;                 /* copy file size */
  730.   tp->fseq  = bbsseq;                   /* copy FILES.BBS line number */
  731.   tp->priv  = fpriv;                    /* copy privilege */
  732.   tp->dl_b  = dlb;                      /* copy download bytes flag */
  733.   tp->dl_t  = dlt;                      /* copy download time flag */
  734.   strcpy(tp->fname, fa->fname);         /* copy filename to new element  */
  735.   tp->fdesc = (desc != NULL) ? desc : NDS;  /* add pointer to desc. */
  736.   return(tp);                           /* return pointer */
  737.   }
  738.  
  739. /* ---------------------------------------- */
  740. /* Add single file-entry to file-chain      */
  741. /* returns: pointer to new element          */
  742. /* ---------------------------------------- */
  743. FILECHAIN *file_element(DOWNPATH *parea,       /* ptr to info this area */
  744. #ifndef __32BIT__
  745.                         FILEFINDBUF *cf)       /* DOS/OS file info block */
  746. #else
  747.                         FILEFINDBUF3 *cf)      /* OS/2 file info block   */
  748. #endif
  749. {
  750.   FILECHAIN *tp;                        /* temporary-pointer             */
  751.  
  752.   tp = (FILECHAIN *)malloc(sizeof(FILECHAIN));
  753.   if (tp == NULL) {
  754.      fprintf(stderr, MSG_MEM, PROGNAME);  /* not enough memory           */
  755.      exit(11);
  756.      }
  757.   memset(tp, '\0', sizeof(FILECHAIN));  /* whole struct zeroes    */
  758.   tp->next_element = NULL;              /* ptr to next                   */
  759.   tp->fseq  = 65535;                    /* default FILES.BBS line number */
  760.   tp->priv  = HIDDEN;                   /* unless FILES.BBS proves otherw*/
  761.   tp->fdesc = NULL;                     /* unless found in FILES.BBS     */
  762.   tp->fpath = NULL;                     /* explicit path in FILES.BBS    */
  763.   tp->parea = parea;                    /* copy pointer to area info     */
  764.   if (cf != NULL) {                     /* file-system info available    */
  765.     tp->wd.date = cf->fdateLastWrite;   /* file date */
  766.     tp->wt.time = cf->ftimeLastWrite;   /* file time */
  767.     tp->cd.date = cf->fdateCreation;
  768.     tp->ct.time = cf->ftimeCreation;
  769.     if (tp->cd.idate > tp->wd.idate)    /* creation date more recent */
  770.       ;                                 /* nothing */
  771.     else if (tp->cd.idate == tp->wd.idate  &&  /* eq dates but */
  772.              tp->ct.itime >  tp->wt.itime)   /* creation time more recent */
  773.       ;                                 /* nothing */
  774.     else {                              /* otherwise and non-HPFS volumes*/
  775.       tp->cd.date = tp->wd.date;
  776.       tp->ct.time = tp->wt.time;
  777.       }
  778.     tp->size  = cf->cbFile;
  779.     strncpy(tp->fname, cf->achName, MAXFN);
  780.     strupr(tp->fname);
  781.     }
  782.   return(tp);
  783.   }
  784.  
  785. /* --------------------------------- */
  786. /* Build comment entry in file-chain */
  787. /* --------------------------------- */
  788. FILECHAIN *add_comment(char  *filename,      /* comment entry (EMPTY) */
  789.                        DOWNPATH  *parea,     /* ptr to area array */
  790.                        char    *fdesc,       /* FILES.BBS line */
  791.                        short   int fpriv,    /* area/file privilege */
  792.                        USHORT  bbsseq)       /* line # in FILES.BBS */
  793. {
  794.   FILECHAIN *tp;                        /* ptr to file-info */
  795.   char   *fd;                           /* ptr to file description */
  796.   char   buftmp[MAXDESC];               /* work buffer */
  797.   short int  k;                         /* counter */
  798.  
  799.   strncpy(buftmp, fdesc, MAXDESC);      /* save desc in buftmp */
  800.   for (k=0; buftmp[k]!='\0'; k += 1) {  /* scan work buffer */
  801.     if (buftmp[k]=='\r' || buftmp[k]=='\n') { /* for CR/LF */
  802.       buftmp[k] = '\0';                 /* replace by end-of-string */
  803.       break;
  804.       }
  805.     }
  806.   tp = file_element(parea, NULL);       /* alloc new element */
  807.   strncpy(tp->fname, filename, MAXFN);  /* EMPTY for comment */
  808.   tp->priv = fpriv;                     /* privilege  */
  809.   tp->fseq = bbsseq;                    /* FILES.BBS line number */
  810.   tp->cmt  = 1;                         /* indicate as comment element */
  811.   fd = malloc(k + 1);                   /* obtain memory for desc */
  812.   if (fd == NULL) {
  813.     fprintf(stderr, MSG_MEM, PROGNAME);    /* not enough memory */
  814.     exit(12);
  815.     }
  816.   else {
  817.     strcpy(fd, buftmp);                 /* store description             */
  818.     tp->fdesc = fd;                     /* pointer to desc               */
  819.     }
  820.   return(tp);                           /* return pointer                */
  821.   }
  822.  
  823. /* ------------------------------------------------------ */
  824. /* supply file-entry with explicit path spec in FILES.BBS */
  825. /* ------------------------------------------------------ */
  826. void  add_pathspec(char  *filespec,     /* ASCIIZ full filespec */
  827.                    FILECHAIN *tp)       /* pointer to file element */
  828. {
  829. #ifndef __32BIT__
  830.   FILESTATUS cf;                        /* file-info buffer              */
  831. #else
  832.   FILESTATUS3 cf;                       /* file-info buffer              */
  833. #endif
  834.   unsigned int  fn_off;                 /* offset filename in pathspec   */
  835.  
  836.   if (
  837. #ifndef __32BIT__
  838.       DosQPathInfo(strupr(filespec), 1, (char *)&cf, sizeof(cf), 0L)
  839. #else
  840.       DosQueryPathInfo(strupr(filespec), 1, &cf, sizeof(cf))
  841. #endif
  842.            != 0) {
  843.     fprintf(stderr, "\tfile %s not found\n", filespec);
  844.     return;                             /* return without info */
  845.     }
  846.   else {
  847.     fn_off = split_fname(filespec);     /* path size */
  848.     strncpy(tp->fname, filespec+fn_off, MAXFN);
  849.     tp->fname[MAXFN-1] = '\0';          /* ensure string termination */
  850.     strupr(tp->fname);                  /* filename in capitals */
  851.     tp->fpath = malloc(fn_off + 1);     /* mem for path */
  852.     if (tp->fpath != NULL) {            /* memory obtained */
  853.       strncpy(tp->fpath, strupr(filespec), fn_off);  /* copy path */
  854.       *(tp->fpath + fn_off) = '\0';     /* end of string */
  855.       }
  856.     tp->wd.date = cf.fdateLastWrite;
  857.     tp->wt.time = cf.ftimeLastWrite;
  858.     tp->cd.date = cf.fdateCreation;
  859.     tp->ct.time = cf.ftimeCreation;
  860.     if (tp->cd.idate > tp->wd.idate)    /* creation date more recent */
  861.       ;                                 /* nothing */
  862.     else if (tp->cd.idate == tp->wd.idate  &&  /* eq dates but */
  863.              tp->ct.itime >  tp->wt.itime)  /* creation time more recent */
  864.       ;                                 /* nothing */
  865.     else {                              /* otherwise and non-HPFS volumes*/
  866.       tp->cd.date = tp->wd.date;
  867.       tp->ct.time = tp->wt.time;
  868.       }
  869.     tp->size  = cf.cbFile;
  870.     return;                             /* back to caller */
  871.     }
  872.   }
  873.  
  874. /* ----------------------------------------------------------- */
  875. /* Remove orphans from chain and release memory of these.      */
  876. /* Returns number of removed orphans (may be all files!)       */
  877. /* ----------------------------------------------------------- */
  878. long int free_orphan(FILECHAIN **c0)    /* start of group */
  879. {
  880.   long int  orp_cnt;                    /* removed-orphan counter */
  881.   FILECHAIN *ca, *cb;                   /* current and next chain pointer*/
  882.  
  883.   orp_cnt = 0;                          /* init removed-orphan counter */
  884.   ca = *c0;                             /* look at first element */
  885.   while (ca != NULL) {                  /* whole group (until break) */
  886.     if (ca->priv >= HIDDEN) {           /* orphan */
  887.       cb = ca->next_element;            /* pointer to next element */
  888.       *c0 = cb;                         /* next element past first orphan */
  889.       free(ca);                         /* drop orphan */
  890.       ++orp_cnt;                        /* bump up orphan count */
  891.       ca = cb;                          /* shift to new first of group */
  892.       }
  893.     else                                /* current first not orphan */
  894.       break;                            /* exit from while-loop */
  895.     }
  896.   while (ca != NULL) {                  /* all after 1st not-orphan */
  897.     cb = ca->next_element;              /* next element */
  898.     if (cb != NULL && cb->priv >= HIDDEN) {  /* orphan */
  899.       ca->next_element = cb->next_element; /* new ptr in current element */
  900.       free(cb);                         /* free memory block */
  901.       ++orp_cnt;                        /* one more dropped */
  902.       }                                 /* note: do not shift current */
  903.     else                                /* not an orphan */
  904.       ca = cb;                          /* shift +1 current element */
  905.     }
  906.   return(orp_cnt);                      /* report # of removed orphans */
  907.   }
  908.  
  909.  
  910. /* ----------------------------------------------------------- */
  911. /* Count orphans                                               */
  912. /* ----------------------------------------------------------- */
  913. long int count_orphans(FILECHAIN *c0)   /* start of group */
  914. {
  915.   long int  orp_cnt;                    /* orphan counter */
  916.   FILECHAIN *ca;                        /* current chain pointer */
  917.  
  918.   orp_cnt = 0;                          /* init removed-orphan counter */
  919.   ca = c0;                              /* first of group */
  920.   while (ca != NULL) {                  /* whole group */
  921.     if (ca->priv >= HIDDEN)             /* orphan */
  922.       ++orp_cnt;                        /* bump up orphan count */
  923.     ca = ca->next_element;              /* pointer to next element */
  924.     }
  925.   return(orp_cnt);                      /* report # of removed orphans */
  926.   }
  927.  
  928. /* ---------------------------------------------------- */
  929. /* Combine consecutive comment lines to a single string */
  930. /* Returns the number of freed chain elements           */
  931. /* ---------------------------------------------------- */
  932. long int combine_comments(void)         /* travels along filechain */
  933. {
  934.   USHORT     cmt_cnt;                   /* freed elements count */
  935.   FILECHAIN  *ca, *cb;                  /* current and next chain pointer*/
  936.   char       *new;                      /* ptr to combined comments */
  937.   unsigned int k,l;                     /* string lengths */
  938.   USHORT     nextseq;                   /* next expected sequence # */
  939.  
  940.   cmt_cnt = 0;                          /* initial freed elements count  */
  941.   if ((ca=first_element) != NULL) {     /* any elements in chain */
  942.     nextseq = 1 + ca->fseq;             /* next consecutive line */
  943.     while ((cb=ca->next_element) != NULL) {  /* all elements */
  944.       if (ca->fname[0] == '\0'  &&      /* comment #1 */
  945.           cb->fname[0] == '\0'  &&      /* comment #2 */
  946.           cb->fseq == nextseq) {        /* consecutive FILES.BBS lines */
  947.         k = strlen(ca->fdesc);          /* take length of 1st comment */
  948.         l = strlen(cb->fdesc);          /* take length of 2nd comment */
  949.         if ((new = (char *)malloc(k+l+2)) != NULL) {  /* mem obtained */
  950.           strcpy(new,ca->fdesc);        /* copy 1st comment */
  951.           strcat(new,"\n");             /* insert newline */
  952.           strcat(new,cb->fdesc);        /* copy 2nd comment */
  953.           free(ca->fdesc);              /* release 1st old comment */
  954.           free(cb->fdesc);              /* release 2nd old comment */
  955.           ca->fdesc = new;              /* ptr to combined desc in cur el*/
  956.           ca->next_element = cb->next_element; /* new ptr in cur el. */
  957.           free(cb);                     /* release 2nd chain-element */
  958.           cmt_cnt += 1;                 /* update combination counter */
  959.           nextseq += 1;                 /* next bbsseq# */
  960.           }
  961.         else {
  962.           ca = cb;                      /* no memory: proceed */
  963.           nextseq = 1 + ca->fseq;
  964.           }
  965.         }
  966.       else {                            /* not 2 consecutive comments */
  967.         ca = cb;                        /* shift +1 current element */
  968.         nextseq = 1 + ca->fseq;
  969.         }
  970.       }
  971.     }                                   /* only orphans */
  972.   return(cmt_cnt);                      /* report # of removed elements  */
  973.   }
  974.  
  975. /* ----------------------------------------- */
  976. /* Check if string could be a filename       */
  977. /* Only needed as distiction from comment.   */
  978. /* Returns NULL if probably not a filespec.  */
  979. /* ----------------------------------------- */
  980. char *parse_fname(char *buf)            /* ptr to FILES.BBS inputline */
  981. {
  982.   char  c0;                             /* first character */
  983.  
  984.   c0 = buf[0];                          /* take first character on line */
  985.   if ( strlen(buf) < 2           ||     /* empty line */
  986.        c0 <= ' '                 ||
  987.        c0 == '\"'                ||
  988.       (c0 >= '+'  && c0 <= '/')  ||
  989.       (c0 >= ':'  && c0 <= '>')  ||
  990.       (c0 >= '['  && c0 <= ']')  ||
  991.        c0 == '|'                 ||
  992.        c0 >= 128)
  993.     return(NULL);                       /* probably not a filename */
  994.   else
  995.     return(buf);
  996.   }
  997.  
  998. /* ----------------------------------- */
  999. /* Split path (if any) from filespec.  */
  1000. /* Returns offset to filename in buf.  */
  1001. /* ----------------------------------- */
  1002. unsigned int split_fname(char *buf)     /* filespecification string */
  1003. {
  1004.   char  *pFile;                         /* pointer */
  1005.   char  tempbuf[2048];                  /* work buffer */
  1006.  
  1007.   strcpy(tempbuf, asciiz(buf));         /* copy first 'word' */
  1008.   pFile = strrchr(tempbuf,'\\');        /* search last backslash */
  1009.   if (pFile == NULL)                    /* no directory spec found */
  1010.     pFile = strrchr(tempbuf,':');       /* search last colon */
  1011.   if (pFile == NULL)                    /* also no drive spec found */
  1012.     return(0);                          /* zero: no explicit path spec'd */
  1013.   else                                  /* explicit pathspec found */
  1014.     return(pFile - tempbuf + 1);        /* offset to filename in buf */
  1015.   }
  1016.  
  1017.  
  1018. /* ------------------------------------- */
  1019. /* Determine if area is within selection */
  1020. /* Returns zero if to be excluded        */
  1021. /* ------------------------------------- */
  1022. int  area_selection(char *name)         /* areaname */
  1023. {
  1024.   int   result;                         /* return value */
  1025.   STRCHAIN *s;                          /* pointer to chain element */
  1026.  
  1027.   result = 1;                           /* included unless in/ex active  */
  1028.  
  1029.   if ((s = incl_area) != NULL) {        /* area_include active */
  1030.     result = 0;                         /* not included unless found */
  1031.     while (s != NULL) {                 /* scan include chain */
  1032.       if (!strnicmp(s->str, name, strlen(s->str))) {  /* to be included */
  1033.         result = 1;                     /* found to be included          */
  1034.         s = excl_area;                  /* pointer to exclude string */
  1035.         if (s != NULL) {                /* exclusion also active */
  1036.           while (s != NULL) {           /* all excluded areas */
  1037.             if (!strnicmp(s->str, name, strlen(s->str))) {
  1038.               result = 0;               /* found to be excluded          */
  1039.               break;                    /* escape from exclude loop      */
  1040.               }
  1041.             s = s->next;                /* pointer to next chain element */
  1042.             }
  1043.           }
  1044.         break;                          /* escape from include loop */
  1045.         }
  1046.       s = s->next;                      /* pointer to next chain element */
  1047.       }
  1048.     }
  1049.   else if ((s = excl_area) != NULL) {   /* explicit exclusion active    */
  1050.     result = 1;                         /* included unless found */
  1051.     while (s != NULL) {                 /* whole EXclude chain */
  1052.       if (!strnicmp(s->str, name, strlen(s->str))) {
  1053.         result = 0;                     /* found to be excluded */
  1054.         break;                          /* escape from exclude loop */
  1055.         }
  1056.       s = s->next;                      /* pointer to next element */
  1057.       }
  1058.     }
  1059.   else
  1060.     result = 1;                         /* no selections: all            */
  1061.   return(result);                       /* return selection result       */
  1062.   }
  1063.  
  1064. /* ----------------------------------------- */
  1065. /* Determine if FILENAME is within selection */
  1066. /* Returns zero if to be excluded            */
  1067. /* ----------------------------------------- */
  1068. int  file_selection(char *name)         /* filename */
  1069. {
  1070.   STRCHAIN *s;                          /* pointer to chain element */
  1071.  
  1072.   if ((s = excl_file) != NULL) {        /* file-exclude active */
  1073.     while (s != NULL) {                 /* whole chain */
  1074.       if (!wild_comp(name, s->str))     /* wildcards compare */
  1075.         return(0);                      /* found: to be excluded */
  1076.       s = s->next;                      /* next chain element */
  1077.       }
  1078.     }
  1079.   return(1);                            /* file to be included */
  1080.   }
  1081.  
  1082.  
  1083. #if defined(__DEBUG__)
  1084. /* =============== */
  1085. /* Dump area array */
  1086. /* =============== */
  1087. USHORT dump_area_array(DOWNPATH _HUGE *aa,  /* pointer to array */
  1088.                        ULONG  m,        /* number of area's */
  1089.                        char *id)        /* log id */
  1090. {
  1091.   ULONG  i;
  1092.   FILE  *log;
  1093.  
  1094.   log = fopen("downsort.log","a");      /* append!                       */
  1095.   fprintf(log,"\n");                    /* newline                       */
  1096.   sep_line(log,'═',79);                 /* separate from previous append */
  1097.   fprintf(log,"(%s) %s\n", today, id);  /* timestamp + id                */
  1098.   fflush(log);
  1099.   for (i=0; i<m; i++) {
  1100.     fprintf(log,"\n%4ld. bytecount=%10lu priv=%5hd anum=%hd filecount=%5lu\n",
  1101.         i+1,
  1102.         aa[i].byte_count,
  1103.         aa[i].priv,
  1104.         aa[i].anum,
  1105.         aa[i].file_count);
  1106.     fprintf(log," name %s\nename %s\npname %s\nf_bbs %s\n desc %s\n",
  1107.         aa[i].name,
  1108.         aa[i].ename,
  1109.         aa[i].pname,
  1110.         aa[i].filesbbs,
  1111.         aa[i].adesc);
  1112.     fprintf(log,"newst %s\n",
  1113.         (aa[i].newest != NULL) ? aa[i].newest->fname : "-");
  1114.     fflush(log);                        /* write buffers before continue */
  1115.     }
  1116.   fclose(log);
  1117.   return(i);
  1118.   }
  1119.  
  1120. /* =============== */
  1121. /* Dump file chain */
  1122. /* =============== */
  1123. ULONG  dump_file_chain(FILECHAIN *f,    /* pointer to first el. of chain */
  1124.                        char *id)        /* log id                        */
  1125. {
  1126.   FILE      *log;
  1127.   ULONG      fc;                        /* element count                 */
  1128.   FILECHAIN *fe;                        /*                               */
  1129.  
  1130.   log = fopen("downsort.log","a");      /* append!                       */
  1131.   fprintf(log,"\n");                    /* newline                       */
  1132.   sep_line(log,'═',79,0);               /* separate from previous append */
  1133.   fprintf(log,"(%s) %s\n\n", today, id);  /* timestamp + id              */
  1134.   fflush(log);                          /* force physical write(s)       */
  1135.   fe = f;                               /* pointer to first element      */
  1136.   fc = 0;                               /* counter=0                     */
  1137.   while(fe != NULL) {
  1138.     if (fe->fname[0] != '\0') {
  1139.       fprintf(log,"%*.*s", MAXANAME, MAXANAME, fe->parea->name); /* area */
  1140.       fprintf(log," %s ", fe->fname);   /* filename                      */
  1141.       }
  1142.     fprintf(log,"%s\n",
  1143.                 (fe->fdesc != NULL) ? fe->fdesc : EMPTY);  /* file desc  */
  1144.     fc++;
  1145.     fe = fe->next_element;
  1146.     }
  1147.   fprintf(log,"\nChain_element_count = %lu\n\n", fc);
  1148.   fclose(log);
  1149.   return(fc);
  1150.   }
  1151. #endif
  1152.  
  1153.