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

  1.  
  2. /* ============================================================= */
  3. /*  Rob Hamerling's MAXIMUS download file scan and sort utility. */
  4. /*  -> DOWNSRV.C                                                 */
  5. /*  -> Collection of general service routines for DOWNSORT.      */
  6. /* ============================================================= */
  7.  
  8. #define INCL_BASE
  9. #define INCL_NOPMAPI
  10. #include <os2.h>
  11.  
  12. #include <ctype.h>
  13. #include <malloc.h>
  14. #include <stdarg.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <time.h>
  19.  
  20. #include "downsort.h"
  21. #include "downfpro.h"
  22.  
  23. /* function prototype of local functions */
  24.  
  25. int  comp_area(DOWNPATH *, DOWNPATH *);
  26.  
  27.  
  28. /* ------------------- */
  29. /* Welcome to the user */
  30. /* ------------------- */
  31. void show_welcome(void)
  32. {
  33.   static char HD[22] = "══════════════════════";
  34.   printf("\n%15s╔%.22s╗\n", "", HD);
  35.   printf("╔%.14s╣ %8.8s version %c.%c%c╠%.15s╗\n",
  36.                  HD,PROGNAME,VERSION,SUBVERS,SUFFIX,HD);
  37.   printf("║  Copyright   ╚%.22s╝   Shareware   ║\n", HD);
  38.   printf("║  %s %-38.38s║\n", MAX,PROGDESC);
  39.   printf("║      by %-13.13s, %-29s║\n", AUTHOR,CITY);
  40.   printf("║%-29s%24s║\n", PHONE, FIDO);
  41.   printf("╚%.22s%.22s%.9s╝\n", HD, HD, HD);
  42.   }
  43.  
  44. /* ======================================================== */
  45. /* Compare two filenames.                                   */
  46. /* First name must be regular "8.3" format filename.        */
  47. /* Second name may contain wildcards.                       */
  48. /* ======================================================== */
  49. int wild_comp(char a[],
  50.               char b[])
  51. {
  52.   int i;
  53.   char na[11], nb[11];                  /* formatted filename fields     */
  54.                                         /* keep 'm for subsequent calls  */
  55.   static char empty_ext[]  = { "..." }; /* files without ext.            */
  56.  
  57.   i = non_wild_init(8,na,a);            /* init non-wild string          */
  58.   switch(a[i]) {                        /* after fileNAME                */
  59.     case ' ' :
  60.     case '\0':
  61.       i = non_wild_init(3,na+8,empty_ext);  /* empty extension           */
  62.       break;
  63.     case '.' :
  64.       i = non_wild_init(3,na+8,a+i+1);  /* process extension             */
  65.       break;
  66.     default:                            /* invalid filename              */
  67.       i = non_wild_init(3,na+8,a+i);    /* pseudo extension              */
  68.       break;
  69.     }
  70.  
  71.   i = wild_init(8,nb,b);
  72.   switch(b[i]) {
  73.     case ' ' :
  74.     case '\0':
  75.       i = wild_init(3,nb+8,empty_ext);  /* empty extension               */
  76.       break;
  77.     case '.' :
  78.       i = wild_init(3,nb+8,b+i+1);      /* process extension             */
  79.       break;
  80.     default:                            /* invalid filename              */
  81.       i = wild_init(3,nb+8,b+i);        /* pseudo extension              */
  82.       break;
  83.     }
  84.  
  85. #if defined(__DEBUG__)
  86.   printf("\n");                         /* debug of wild compare         */
  87.   for (i=0; i<11; ++i)
  88.     printf("%c", (na[i] != '\0') ? na[i] : ' ');
  89.   printf(" ");
  90.   for (i=0; i<11; ++i)
  91.     printf("%02.2X", na[i]);
  92.   printf("   ");
  93.   for (i=0; i<11; ++i)
  94.     printf("%c", (nb[i] != '\0') ? nb[i] : ' ');
  95.   printf(" ");
  96.   for (i=0; i<11; ++i)
  97.     printf("%02.2X", nb[i]);
  98. #endif
  99.  
  100. /* for (i=0; i<11 && (na[i]==nb[i] || na[i]=='\0' || nb[i]=='\0'); i++); */
  101.   for (i=0; i<11 && (na[i]==nb[i] || nb[i]=='\0'); i++);
  102.  
  103. #if defined(__DEBUG__)
  104.   printf(" =#=%d",i);                    /* debug of wild compare        */
  105. #endif
  106.   if (i>=11)                             /* strings equal                */
  107.     return(0);
  108.   else
  109.     return(na[i]-nb[i]);
  110.   }
  111.  
  112. /* ============================================ */
  113. /* Init string for wild-card filenames compare. */
  114. /* Translation to UPPER case.                   */
  115. /* ============================================ */
  116. int wild_init(short int n,
  117.               char p[],
  118.               char q[])
  119. {
  120.   int i,j;
  121.  
  122.   i=j=0;
  123.   while (i<n) {                         /* process string 'q'            */
  124.     switch(q[j]) {
  125.       case '?':                         /* single wild char              */
  126.         p[i++] = '\0';                  /* matches with any other char   */
  127.         j++;                            /* proceed with next char        */
  128.         break;
  129.       case '.':                         /* end of filespec-part          */
  130.       case ' ':                         /* logical end of string         */
  131.       case '\0':                        /* end of string                 */
  132.         while (i<n)                     /* fill                          */
  133.           p[i++] = '.';                 /* insert filler chars           */
  134.         break;
  135.       case '*':                         /* wild string                   */
  136.         while (i<n)                     /* fill                          */
  137.           p[i++] = '\0';                /* matches with any other char   */
  138.         j++;                            /* to next char                  */
  139.         break;
  140.       default:                          /* 'normal' characters           */
  141.         p[i] = (char) toupper(q[i]);    /* convert to UPPERcase and copy */
  142.         i++; j++;                       /* proceed with next char        */
  143.         break;
  144.       }
  145.     }
  146.  
  147.   return(j);                            /* displ. of last examined char  */
  148.   }
  149.  
  150. /* ==================================================== */
  151. /* Init string for non-wild-card filenames compare.     */
  152. /* No wild-cards expected, translation to upper case.   */
  153. /* ==================================================== */
  154. int non_wild_init(short int n,
  155.                   char p[],
  156.                   char q[])
  157. {
  158.   int i,j;
  159.  
  160.   i=j=0;
  161.   while (i<n) {                         /* process string 'q'            */
  162.     if (q[j]=='.' || q[j]==' ' || q[j]=='\0') {
  163.       p[i] = '.';                       /* insert filler char            */
  164.       i++;                              /* rest filler (no j-increment!) */
  165.       }
  166.     else {
  167.       p[i] = (char) toupper(q[i]);      /* to UPPERcase and copy         */
  168.       i++; j++;                         /* proceed with next char        */
  169.       }
  170.     }
  171.  
  172.   return(j);                            /* displ. of last examined char  */
  173.   }
  174.  
  175. /* ------------------ */
  176. /* Produce time stamp */
  177. /* ------------------ */
  178. char *sys_date(char t[])
  179. {
  180.   long int secs_now;                    /* relative time value           */
  181.   char *buf;                            /* pointer to time string        */
  182.  
  183.   time(&secs_now);                      /* get current time              */
  184.   buf = ctime(&secs_now);               /* convert to char string        */
  185.   strcpy(t, buf);                       /* copy string                   */
  186.   t[16] = '\0';                         /* undo secs, year and newline   */
  187.   return(t);                            /* pointer to buffer             */
  188.   }
  189.  
  190. /* ------------------------------------------------- */
  191. /* Transform file-date into a  9-char string         */
  192. /* COUNTRY format mm-dd-yy (USA) or dd-mm-jj (EUR)   */
  193. /* ------------------------------------------------- */
  194. char *f_date(FDATE date)
  195. {
  196.   static char string[9];                /* work buffer                   */
  197.   sprintf(string,"%2u%s%02u%s%02u",
  198.          ((c_info.fsDateFmt == 0) ? date.month : date.day),
  199.            c_info.szDateSeparator,
  200.          ((c_info.fsDateFmt == 0) ? date.day : date.month),
  201.            c_info.szDateSeparator,
  202.          (date.year+80)%100);           /* allow 2 digits!               */
  203.   return(string);
  204.   }
  205.  
  206. /* ------------------------------------ */
  207. /* Calculate file-age in number of days */
  208. /* ------------------------------------ */
  209. int file_age(FILECHAIN *f)
  210. {
  211.   static ULONG mon_tab[] = {
  212.                0,0,31,59,90,120,151,181,212,243,273,304,334,334,334,334};
  213.                                         /* ignore leap year February! */
  214.   ULONG  fday;
  215.   FDATE  fd;                            /* copy of filedate */
  216.  
  217.   fd = f->cd.date;                      /* copy most recent(!) file date */
  218.   fd.year = min(fd.year, 57);          /* max fileyear supported */
  219.   fday    = ((fd.year+10)*1461+1)/4 + mon_tab[fd.month] + fd.day - 1;
  220.   return((int)(time(NULL)/86400L - fday));  /* file age in days */
  221.   }
  222.  
  223. /* =================== */
  224. /* determine file-age  */
  225. /* =================== */
  226. char  file_age_ind(FILECHAIN *f)
  227. {
  228.   int age;
  229.  
  230.   age = file_age(f);                    /* get file age */
  231.   if (age>30)
  232.     return(' ');                        /* older than a month */
  233.   else {
  234.     if (age>7)                          /* older than 7 days */
  235.       return(DAYS_30);
  236.     else {
  237.       if (age>=0)                       /* non-negative negative age     */
  238.         return(DAYS_7);                 /* a week                        */
  239.       else
  240.         return('-');                    /* negative age                  */
  241.       }
  242.     }
  243.   }
  244.  
  245. /* -------------------------------------------------- */
  246. /* Transform file-size and date into a 15-byte string */
  247. /* Size in K (rounded to next higher number KBytes)   */
  248. /* Date format as inf_date(), plus age indicator.     */
  249. /* If date zero: pointer to 'offline'-text.           */
  250. /* -------------------------------------------------- */
  251. char *f_size_date(FILECHAIN *f)
  252. {
  253.   static char string[16];               /* work buffer */
  254.   ULONG  akb;                           /* filesize in KB */
  255.  
  256.   if (f->wd.date.day) {                 /* date non-zero */
  257.     akb = (f->size+1023)/1024;          /* (rounded) Kbytes value */
  258.     sprintf(string,"%4lu%c ",           /* filesize */
  259.            (akb <= 9999) ? akb : (akb+512)/1024,
  260.            (akb <= 9999) ? 'K' : 'M');
  261.     strcat(string, f_date(f->wd.date));  /* add formatted date */
  262.     string[14] = file_age_ind(f);       /* add age ind. */
  263.     string[15] = '\0';                  /* end of string */
  264.     return(string);                     /* for 'online' file */
  265.     }
  266.   else
  267.     return(OFFLINE);                    /* for 'offline' file */
  268.   }
  269.  
  270. /* -------------------------------------------------------------- */
  271. /* Transform file-time into string (hh:mm:ssa)                    */
  272. /* COUNTRY format hh:mm:ssa (12 hr USA) or hh-mm-ss  (24 hr EUR)  */
  273. /* -------------------------------------------------------------- */
  274. char *f_time(FTIME tim)
  275. {
  276.   static char ftime[7] = {'\0'};        /* work buffer                    */
  277.   sprintf(ftime,"%2u%s%02u%c%c",
  278.           (c_info.fsTimeFmt==0 && tim.hours>12) ? tim.hours-12 : tim.hours,
  279.            c_info.szTimeSeparator,
  280.           tim.minutes,
  281.           ((c_info.fsTimeFmt==0) ? ((tim.hours>11) ? 'p' : 'a') : ' '),
  282.           '\0');
  283.   return(ftime);
  284.   }
  285.  
  286. /* ------------------------------ */
  287. /* Build pointer-array for sorts  */
  288. /* ------------------------------ */
  289. FILECHAIN * _HUGE *prep_sort(ULONG      cnt,
  290.                              FILECHAIN *chn)
  291. {
  292.   ULONG       i;                        /* counter                       */
  293.   FILECHAIN   * _HUGE *dm;              /* pointer to huge array         */
  294.   FILECHAIN  *ca;                       /* pointer to fileinfo struct.   */
  295.  
  296. #ifndef __32BIT__
  297.   dm = (FILECHAIN * huge *)halloc(cnt, sizeof(FILECHAIN *));
  298. #else
  299.   dm = (FILECHAIN **)malloc(cnt * sizeof(FILECHAIN *));
  300. #endif
  301.   if (dm == NULL) {                     /* not enough memory             */
  302.     printf(MSG_MEM,PROGNAME);
  303.     exit(11);
  304.     }
  305.   ca = chn;                             /* ptr to first file-info        */
  306.   for (i=0; ca != NULL; i++) {          /* init sort array               */
  307.     dm[i] = ca;
  308.     ca = ca->next_element;
  309.     }
  310.   return(dm);
  311.   }
  312.  
  313. /* ====================================================== */
  314. /* compare for sort on file date+time, filename, areaname */
  315. /* ====================================================== */
  316. int  sort_new(FILECHAIN *p,
  317.               FILECHAIN *q)
  318. {
  319.   int  rc;                              /* returncode */
  320.   unsigned int x, y;                    /* calculation results */
  321.  
  322.   x = min(p->wd.idate, p->cd.idate);    /* file date p */
  323.   y = min(q->wd.idate, q->cd.idate);    /* file date q */
  324.   if ( x == y ) {                       /* equal file dates */
  325.     x = min(p->wt.itime, p->ct.itime);  /* file time p */
  326.     y = min(q->wt.itime, q->ct.itime);  /* file time q */
  327.     if ( x == y ) {                     /* equal file times */
  328.       rc = stricmp(p->fname,q->fname);
  329.       if (rc)                           /* unequal filenames */
  330.         return(rc);
  331.       else
  332.         return(comp_area(p->parea, q->parea));  /* areaname */
  333.       }
  334.     else
  335.       return( (int)(y - x) );           /* time difference */
  336.     }
  337.   else
  338.     return( (int)(y - x) );             /* date difference */
  339.   }
  340.  
  341. /* ========================================================== */
  342. /* Compare for sort on case-insensitive filename + area-name  */
  343. /* ========================================================== */
  344. int  sort_gbl(FILECHAIN *p,
  345.               FILECHAIN *q)
  346. {
  347.   int   rc;
  348.  
  349.   rc = stricmp(p->fname, q->fname);
  350.   if (rc)                               /* unequal filename              */
  351.     return(rc);
  352.   else
  353.     return(comp_area(p->parea, q->parea));
  354.   }
  355.  
  356. /* ======================================================= */
  357. /* Compare for sort of filearray on filename + area-name   */
  358. /* (for file collect process)                              */
  359. /* ======================================================= */
  360. int  sort_fcol(const void *p,
  361.                const void *q)
  362. {
  363.   int   rc;
  364.   FILECHAIN *a, *b;
  365.  
  366.   a = *(FILECHAIN **)p;
  367.   b = *(FILECHAIN **)q;
  368.   rc = strcmp(a->fname, b->fname);
  369.   if (rc)                               /* unequal filename              */
  370.     return(rc);
  371.   else
  372.     return(comp_area(a->parea, b->parea));
  373.   }
  374.  
  375. /* =================================================== */
  376. /* Compare for sort of area-array on download pathname */
  377. /* =================================================== */
  378. int  sort_path(const void *p,
  379.                const void *q)
  380. {
  381.   int  rc;
  382.   DOWNPATH *a, *b;
  383.  
  384.   a = (DOWNPATH *)p;
  385.   b = (DOWNPATH *)q;
  386.   if ((rc = stricmp(a->pname, b->pname)) == 0)  /* download pathname     */
  387.     rc = stricmp(a->name, b->name);     /* area name                     */
  388.   return(rc);
  389.   }
  390.  
  391. /* ======================================== */
  392. /* Compare for sort on areaname + filename  */
  393. /* ======================================== */
  394. int   sort_all(FILECHAIN *p,
  395.                FILECHAIN *q)
  396. {
  397.   int   rc;
  398.  
  399.   rc = comp_area(p->parea, q->parea);
  400.   if (rc)                                 /* unequal areaname            */
  401.     return(rc);
  402.   else
  403.     return(stricmp(p->fname, q->fname));  /* filename                    */
  404.   }
  405.  
  406. /* ======================================================== */
  407. /* Compare for sort on areaname + file-date/time + filename */
  408. /* ======================================================== */
  409. int   sort_al2(FILECHAIN *p,
  410.                FILECHAIN *q)
  411. {
  412.   int  rc;
  413.   unsigned int x, y;
  414.  
  415.   rc = comp_area(p->parea, q->parea);
  416.   if (rc)                               /* unequal area-name */
  417.     return(rc);
  418.   else {
  419.     x = min(p->wd.idate, p->cd.idate);
  420.     y = min(q->wd.idate, q->cd.idate);
  421.     if (x==y) {                         /* equal file dates */
  422.       x = min(p->wt.itime, p->ct.itime);
  423.       y = min(q->wt.itime, q->ct.itime);
  424.       if (x==y)                         /* equal file times */
  425.         return(stricmp(p->fname, q->fname)); /* filename */
  426.       else
  427.         return( (int)(y-x) );           /* unequal file dates */
  428.       }
  429.     else
  430.       return( (int)(y-x) );             /* unequal file dates */
  431.     }
  432.   }
  433.  
  434. /* ======================================================== */
  435. /* Compare for sort on areaname + FILES.BBS sequence number */
  436. /* ======================================================== */
  437. int  sort_akp(FILECHAIN *p,
  438.               FILECHAIN *q)
  439. {
  440.   int   rc;
  441.  
  442.   rc = comp_area(p->parea, q->parea);
  443.   if (rc)                               /* unequal areacode              */
  444.     return(rc);
  445.   else {
  446.     if (p->fseq != q->fseq)             /* unequal sequence number       */
  447.       return( (int)((p->fseq < q->fseq) ? -1 : +1) );
  448.     else
  449.       return(0);                        /* equal sequence number         */
  450.     }
  451.   }
  452.  
  453. /* =================================================== */
  454. /* Compare for sort on areaname + privilege + filename */
  455. /* No sort on privilege if below area-privilege.       */
  456. /* =================================================== */
  457. int  sort_fil(FILECHAIN *p,
  458.               FILECHAIN *q)
  459. {
  460.   int   rc;
  461.  
  462.   rc = comp_area(p->parea, q->parea);
  463.   if (rc)                               /* unequal areacode              */
  464.     return(rc);
  465.   else {
  466.     if (p->priv <= p->parea->priv &&    /* both within area priv         */
  467.         q->priv <= q->parea->priv)
  468.       return(stricmp(p->fname, q->fname));  /* sort on filename          */
  469.     else if (p->priv == q->priv)        /* same privilege                */
  470.       return(stricmp(p->fname, q->fname));  /* filename                  */
  471.     else
  472.       return( (int)(p->priv - q->priv) ); /* file priv                   */
  473.     }
  474.   }
  475.  
  476. /* ========================================= */
  477. /* Compare for sort on areaname of AREA-info */
  478. /* ========================================= */
  479. int  sort_summ(const void *p,
  480.                const void *q)
  481. {
  482.   DOWNPATH *a, *b;
  483.  
  484.   a = (DOWNPATH *)p;
  485.   b = (DOWNPATH *)q;
  486.   return( comp_area(a, b) );
  487.   }
  488.  
  489. /* ========================================= */
  490. /* Compare for sort on areaname of AREA-info */
  491. /* ========================================= */
  492. int   comp_area(DOWNPATH *p,
  493.                 DOWNPATH *q)
  494. {
  495.   int x,y;                              /* sequence number in chain */
  496.   STRCHAIN *s, *t;                      /* pointer to chain element */
  497.  
  498.   switch(area_seq) {
  499.     case KEEPSEQ: return(p->anum - q->anum);
  500.                   break;
  501.     case ALPHA:   return(stricmp(p->name, q->name));
  502.                   break;
  503.     case INCLUDE: if ((s=incl_area) != NULL) {  /* areaINclude used  */
  504.                     for (x=0; s != NULL   &&
  505.                               strnicmp(s->str, p->name, strlen(s->str));
  506.                                 x++, s=s->next);  /* just determine x */
  507.                     t = incl_area;              /* head of chain */
  508.                     for (y=0; t != NULL   &&
  509.                               strnicmp(t->str, q->name, strlen(t->str));
  510.                                 y++, t=t->next);  /* just determine y */
  511.                     if (x!=y)           /* if not equal */
  512.                       return(x - y);    /* relative position */
  513.                     }                   /* may fall through */
  514.     case GROUP:                         /* default */
  515.     default:      return(stricmp(p->ename, q->ename));
  516.                   break;
  517.     }
  518.   }
  519.  
  520. /* ---------------------------- */
  521. /* Sort file-info pointer array */
  522. /* ---------------------------- */
  523. void psort(FILECHAIN * _HUGE *arr,
  524.            long int left,
  525.            long int right,
  526.            int (*comp)(FILECHAIN *, FILECHAIN *))
  527. {
  528.   long int  asc, des;
  529.   FILECHAIN *ref, *tmp, *wall;
  530.  
  531.   if ((right-left) < 1)                 /* too few elements              */
  532.     return;
  533.  
  534.   asc = left;                           /* left 'wall'                   */
  535.   des = right;                          /* right 'wall'                  */
  536.   ref = arr[(left+right)/2];            /* reference value               */
  537.  
  538.   do {
  539.     wall = arr[asc];                    /* copy pointer */
  540.     while (comp(wall, ref) < 0)         /* move right                    */
  541.       wall = arr[++asc];                /* copy pointer */
  542.     wall = arr[des];                    /* copy pointer */
  543.     while (comp(wall, ref) > 0)         /* move left                     */
  544.       wall = arr[--des];
  545.     if (asc <= des) {                   /* swap                          */
  546.       tmp = arr[des];
  547.       arr[des--] = arr[asc];
  548.       arr[asc++] = tmp;
  549.       }
  550.     } while (asc <= des);
  551.  
  552.   if ((des-left) < (right-asc)) {        /* sort smaller part first        */
  553.     if (left < des)
  554.       psort(arr, left, des, comp);
  555.     if (right > asc)
  556.       psort(arr, asc, right, comp);
  557.     }
  558.   else {                                 /* sort now larger part */
  559.     if (right > asc)
  560.       psort(arr, asc, right, comp);
  561.     if (left < des)
  562.       psort(arr, left, des, comp);
  563.     }
  564.   }
  565.  
  566. /* ============================================ */
  567. /* Compare for newest acquisition in ALL-list   */
  568. /* ============================================ */
  569. FILECHAIN *new_acq(FILECHAIN *p,
  570.                    FILECHAIN *q)
  571. {
  572.   unsigned int x, y;
  573.  
  574.   if (q==NULL  ||  q->cmt==1)           /* first or comment entry */
  575.     return(p);                          /* then return first */
  576.   x = min(p->wd.idate, p->cd.idate);
  577.   y = min(q->wd.idate, q->cd.idate);
  578.   if (x==y) {                           /* equal file dates */
  579.     x = min(p->wt.itime, p->ct.itime);
  580.     y = min(q->wt.itime, q->ct.itime);
  581.     return((x>y) ? p : q);              /* return most recent */
  582.     }
  583.   else
  584.     return((x>y) ? p : q);              /* return most recent */
  585.   }
  586.  
  587. /* ---------------------------------------------------- */
  588. /* Determine if file belongs to collection of this list */
  589. /* ---------------------------------------------------- */
  590. int  rpt_coll(FILECHAIN *fx,            /* ptr to file info */
  591.               LISTPARM  *ls,            /* list specifications */
  592.               int       ign)            /* ignore file age (boolean) */
  593. {
  594.   long  age,                            /* file age in days */
  595.         maxage;                         /* maximum allowed file age */
  596.  
  597.   if (fx->priv <= ls->priv) {           /* privilege OK */
  598.     if ((ls->userkeys ^ (fx->parea->filelock | ls->userkeys)) == 0) {
  599.                                         /* have keys for all locks */
  600.       if (fx->fname[0] != '\0') {       /* file name present */
  601.         if (ls->listflag==' ' || ign==FALSE)  /* no selection on file age */
  602.           return(1);                    /* no: file may be listed */
  603.         else {
  604.           age = file_age(fx);           /* file age in days */
  605.           if (age >= 0) {               /* probably valid file date */
  606.             maxage = ls->max_fil *      /* max file age */
  607.                        ((ls->listflag == 'W') ? 7 :
  608.                         ((ls->listflag == 'M') ? 30 : 1));
  609.             return (age <= maxage);     /* true if 'new' */
  610.             }
  611.           else                          /* probably invalid date */
  612.             return(ls->rubbflag != RUBBDATE);    /* negative date allowed */
  613.           }
  614.         }
  615.       else if (ls->sortflag == KEEPSEQ) /* comments to be included */
  616.         return(1);
  617.       }
  618.     }
  619.   return(0);                            /* not member of collection */
  620.   }
  621.  
  622. /* ---------------------------------------------------- */
  623. /* Determine the number of entries to be included in    */
  624. /* further processing (for re-sorting and/or reporting).*/
  625. /* Array is supposed to be sorted in descending date.   */
  626. /* Returns how many entries (counted from begin of      */
  627. /* array) should take part in further processing        */
  628. /* (sorting, but not necessarily reporting!)            */
  629. /* --------------------------------------------------─- */
  630. ULONG  rpt_count(FILECHAIN * _HUGE *dm,   /* ptr to file info array */
  631.                LISTPARM  *ls,           /* list specs */
  632.                FILECHAIN **cn)          /* newest file in collection */
  633. {
  634.   ULONG  i, j;                          /* counters */
  635.   short int maxage;                     /* allowed file age in days */
  636.   FILECHAIN *fx;                        /* local pointer */
  637.  
  638.   maxage = ls->max_fil *                /* when file age limit */
  639.              ((ls->listflag == 'W') ? 7 :
  640.               ((ls->listflag == 'M') ? 30 : 1));
  641.   *cn = NULL;                           /* none yet */
  642.   for (i=j=0; i<file_total_count; i++) {  /* complete collection */
  643.     fx = dm[i];                         /* copy pointer */
  644.     if (rpt_coll(fx, ls, TRUE)) {       /* check if to be listed */
  645.       j++;                              /* net counter */
  646.       *cn = new_acq(fx, *cn);           /* most recent within collection */
  647.       }
  648.     if (ls->listflag == ' ') {          /* limit: number of entries */
  649.       if (j > ls->max_fil)              /* enough collected */
  650.         break;                          /* escape from loop */
  651.       }
  652.     else {                              /* limit: file age */
  653.       if (file_age(fx) > maxage)        /* enough collected */
  654.         break;                          /* escape from loop */
  655.       }
  656.     }
  657.   return(i);                            /* gross size of collection */
  658.   }
  659.  
  660. /* =================================== */
  661. /* Include a text file into a report.  */
  662. /* Output file is supposed to be open! */
  663. /* =================================== */
  664. void file_incl(FILE   *pfo,             /* output file pointer           */
  665.                LISTPARM *ls)            /* filelist id                   */
  666. {
  667.   char  buf[MAXRCD];                    /* I/O buffer                    */
  668.   FILE *pfi;                            /* input file pointer            */
  669.  
  670.   if (ls->incl_fspec != NULL) {         /* filespec present              */
  671.     if ((pfi = fopen(ls->incl_fspec,"r")) != NULL) {
  672.       while (fgets(buf, MAXRCD, pfi) != NULL) { /* all input records     */
  673.         if (ls->id != P_FIL)            /* all but FILES.BBS             */
  674.           fputs(buf, pfo);              /* "asis"                        */
  675.         else                            /* for FILES.BBS output          */
  676.           fprintf(pfo,"%s %s", FILPREFX, buf);  /* special prefix        */
  677.         }
  678.       fclose(pfi);
  679.       }
  680.     else {
  681.       if (oper_mode == VERBOSE)         /* report only in verbose mode   */
  682.         printf(MSG_TRL,ls->incl_fspec);  /* file not included            */
  683.       }
  684.     }
  685.   }
  686.  
  687. /* --------------------------------------------------------------- */
  688. /* Produce file description parts for first and continuation lines */
  689. /* --------------------------------------------------------------- */
  690. void desc_part(FILE *pf,                /* output file pointer           */
  691.                char *desc,              /* complete description          */
  692.                unsigned int l1,         /* length 1st part of descr.     */
  693.                unsigned int l2,         /* length of subsequent parts    */
  694.                LISTPARM *ls)            /* report structure index        */
  695. {
  696.   unsigned int k,n;                     /* length of part of string      */
  697.   char *p;                              /* pointer to output string      */
  698.  
  699.   if ((k=strsubw(desc, &p, l1)) != 0) { /* length (max = l1)             */
  700.     if (ls->wrapflag != WRAP) {      /* truncate                      */
  701.       n = strlen(desc);                 /* total string length           */
  702.       k = (l1 > n) ? n : l1;            /* shortest of l1 and n          */
  703.       }
  704.     fprintf(pf,"%-.*s\n", k, p);        /* (1st part of) string          */
  705.     if (ls->wrapflag==WRAP) {           /* wrapping requested            */
  706.       while ((k=strsubw(p+k, &p, l2)) > 0) {  /* still descriptions parts*/
  707.         fprintf(pf,"%*s%-.*s\n", 79-l2,"", k, p); /* 2nd+ descr parts  */
  708.         }
  709.       }
  710.     }
  711.    else                                 /* no descr: should not occur */
  712.      fprintf(pf,"\n");                  /* at least new line */
  713.   }
  714.  
  715. /* ============================================================== */
  716. /* routine to select a substring while skipping leading blanks    */
  717. /*         and ending within the boundaries on a word-end.        */
  718. /*         Truncate if single word. Respect NL as end of string.  */
  719. /*         Return strlen,  0 for NULL-pointer.                    */
  720. /* ============================================================== */
  721. int strsubw(char *a,
  722.             char **b,
  723.             unsigned int m)
  724. {
  725.   int i,j,k;                            /* counters                      */
  726.  
  727.   if (a==NULL)                          /* NULL pointer                  */
  728.     return(0);                          /* no string                     */
  729.  
  730.   for (i=0; a[i] == ' '; ++i);          /* skip leading blanks           */
  731.   a = *b = a+i;                         /* offset to first non-blank char*/
  732.   for (i=0; a[i] != '\0' && a[i]!='\r' && a[i]!='\n'; ++i); /* search end*/
  733.   if (i==0)                             /* nothing left                  */
  734.     return(0);                          /* end!                          */
  735.  
  736.   for (k=0; k<m && k<i; ++k);           /* maximum substring             */
  737.   if (k<i) {                            /* there is more in string       */
  738.     if (a[k]==' ' || a[k]=='\r' || a[k]=='\n' || a[k]=='\0');  /*word end*/
  739.     else {
  740.       for (j=k-1; j>0 && a[j]!=' '; --j); /* try to remove 'split' word  */
  741.       if (j>0)                          /* any space found?              */
  742.         k = j;                          /* OK, else split!               */
  743.       }
  744.     }
  745.   for (; k>0 && a[k-1] == ' '; --k);    /* remove trailing blanks        */
  746.   return(k);                            /* return length of substring    */
  747.                                         /* b contains start-point        */
  748.   }
  749.  
  750. /* ====================================================== */
  751. /* Function to locate next non-blank character in buffer. */
  752. /* Returns pointer to word or NULL-ptr if no next word.   */
  753. /* ====================================================== */
  754. char *next_word(char *line)
  755. {
  756.   unsigned int i;
  757.  
  758.   for (i=0; line[i]!=' '  &&            /* skip non-blanks               */
  759.             line[i]!='\r' &&
  760.             line[i]!='\n' &&
  761.             line[i]!='\0'; ++i);
  762.   for (   ; line[i]==' '; ++i);         /* skip blanks                   */
  763.   if (line[i] != '\0' &&
  764.       line[i] != '\r' &&
  765.       line[i] != '\n')
  766.     return(line+i);                     /* next word found               */
  767.   else
  768.     return(NULL);                       /* NULL if no next word          */
  769.   }
  770.  
  771. /* =========================================== */
  772. /* Function to make ASCIIZ string of ONE word. */
  773. /* =========================================== */
  774. char *asciiz(char *buf)
  775. {
  776.   unsigned short int i;
  777.  
  778.   for (i=0; buf[i] != ' '  &&           /* end copy at first blank       */
  779.             buf[i] !='\r'  &&           /* CR character                  */
  780.             buf[i] !='\n'  &&           /* LF character                  */
  781.             buf[i] !='\0'; ++i)         /* or end of string              */
  782.     buf2[i] = buf[i];                   /* copy                          */
  783.   buf2[i] = '\0';                       /* end of string                 */
  784.   return(buf2);                         /* pointer to ASCIIZ string      */
  785.   }
  786.  
  787. /* ============================================ */
  788. /* Function to strip off AVATAR codes in string */
  789. /* ============================================ */
  790. char *strava(char *buf)
  791. {
  792.   unsigned short int i, j, k, x;        /* counters                      */
  793.  
  794.   k = strlen(buf);                      /* length of source string       */
  795.   for (i=j=0; i < k; ) {                /* whole input string            */
  796.     switch(buf[i]) {
  797.       case AVA_G:
  798.       case AVA_H:
  799.       case AVA_I:
  800.       case AVA_L: ++i; break;
  801.       case AVA_Y: for (x=0; x<buf[i+2]; x++)
  802.                     buf2[j++] = buf[i+1];      /* repeat                 */
  803.                   i += 3;
  804.                   break;
  805.       case AVA_V: switch(buf[i+1]) {
  806.                     case AVA_B:
  807.                     case AVA_C:
  808.                     case AVA_D:
  809.                     case AVA_E:
  810.                     case AVA_F:
  811.                     case AVA_G:
  812.                     case AVA_I:
  813.                     case AVA_N: i += 2; break;
  814.                     case AVA_A: i += 3; break;
  815.                     case AVA_H: i += 4; break;
  816.                     case AVA_L: i += 5; break;
  817.                     case AVA_M: i += 6; break;
  818.                     case AVA_J:
  819.                     case AVA_K: i += 7; break;
  820.                     case AVA_Y: i += 2; break;
  821.                     default: buf2[j++]=buf[i++]; break;
  822.                     }
  823.                   break;
  824. /* (5.8)  case '-':   (i==0) ? i++ : (buf2[j++] = buf[i++]);  */
  825. /* (5.8)               break;                                 */
  826.       default:    buf2[j++] = buf[i++];
  827.                   break;
  828.       }
  829.     }
  830.   buf2[j] = '\0';                       /* end of string                 */
  831.   return(buf2);                         /* pointer to ASCIIZ string      */
  832.   }
  833.  
  834. /* ========================================================= */
  835. /* creates filecount and bytecount per area within privilege */
  836. /*   and pointer to most recent file in area                 */
  837. /* returns total filecount all area's within priv.           */
  838. /* should be run before calling other count_functions below. */
  839. /* ========================================================= */
  840. ULONG  preproc_area(DOWNPATH  _HUGE *area,
  841.                     FILECHAIN * _HUGE *dm,
  842.                     LISTPARM  *ls)
  843. {
  844.   USHORT   i;                           /* area count */
  845.   ULONG    j;                           /* file count */
  846.   ULONG    fpc;                         /* total filecount within priv */
  847.   FILECHAIN *fx;                        /* local pointer to file info */
  848.   DOWNPATH  *ax;                        /* local pointer to area info */
  849.  
  850.   for (i=0; i<area_total_count; i++) {  /* all area's in array */
  851.     ax = &area[i];                      /* pointer to area info */
  852.     ax->file_count = 0;                 /* init filecount per area */
  853.     ax->byte_count = 0L;                /* init bytecount per area */
  854.     ax->newest = NULL;                  /* null most-recent file */
  855.     }
  856.   for (j=0; j<file_total_count; j++) {  /* scan file-chain */
  857.     fx = dm[j];                         /* copy pointer */
  858.     if (rpt_coll(fx, ls, FALSE)  &&     /* ignore file age */
  859.         fx->cmt == 0) {                 /* not a comment entry */
  860.       ax = fx->parea;                   /* copy pointer */
  861.       ax->file_count++;                 /* increment area filecount */
  862.       ax->byte_count += fx->size;       /* add filesize */
  863.       ax->newest = new_acq(fx, ax->newest);  /* keep newest file */
  864.       }
  865.     }
  866.   fpc = 0;                              /* init total filecount */
  867.   for (i=0; i<area_total_count; i++) {  /* all area's in array */
  868.     ax = &area[i];                      /* copy pointer */
  869.     fpc += ax->file_count;              /* sum of area file_counts */
  870.     }
  871.   return(fpc);                          /* return total file_count */
  872.   }
  873.  
  874. /* =========================================== */
  875. /* count areas within privilege for top-header */
  876. /* works only correctly after count_priv_files */
  877. /* =========================================== */
  878. USHORT count_areas(DOWNPATH _HUGE *area,
  879.                           short int p)
  880. {
  881.   USHORT i,j;
  882.  
  883.   for (i=j=0; i<area_total_count; i++) {  /* whole area-array            */
  884.     if (area[i].priv <= p       &&      /* area within privilege         */
  885.         area[i].file_count > 0)         /* any files                     */
  886.       ++j;                              /* add to 'active' area_count    */
  887.     }
  888.   return(j);                            /* return areas within priv.     */
  889.   }
  890.  
  891. /* ================================================ */
  892. /* (re-)Count files within privilege for top-header */
  893. /*   (file_priv_count already returns file-count)   */
  894. /* Works only correctly after preproc_area().       */
  895. /* ================================================ */
  896. ULONG  count_files(DOWNPATH _HUGE *area)
  897. {
  898.   ULONG i,f;
  899.  
  900.   for (i=f=0; i<area_total_count; i++)  /* scan area array */
  901.     f += area[i].file_count;            /* add to file_count */
  902.   return(f);                            /* return files within priv. */
  903.   }
  904.  
  905. /* =========================================== */
  906. /* count bytes within privilege for top-header */
  907. /* works only correctly after count_priv_files */
  908. /* =========================================== */
  909. ULONG  count_bytes(DOWNPATH _HUGE *area)
  910. {
  911.   USHORT i;                             /* counter                       */
  912.   ULONG  b;                             /* byte counter                  */
  913.  
  914.   for (i=0,b=0; i<area_total_count; i++)  /* scan area array            */
  915.     b += area[i].byte_count;            /* add to byte_count             */
  916.   return(b);                            /* return bytes within priv.     */
  917.   }
  918.  
  919. /* ==================================== */
  920. /* insert title lines from DOWNSORT.CFG */
  921. /* ==================================== */
  922. void insert_title(FILE *pf,
  923.                   STRCHAIN *title,
  924.                   int  ipf)             /* if not zero: call stripf() */
  925. {
  926.   if (title != NULL) {                  /* any title lines present */
  927.     do {
  928.       fprintf(pf,"%s\n", (ipf) ? stripf(title->str) : title->str);
  929.       title = title->next;
  930.       } while (title != NULL);
  931.     }
  932.   }
  933.  
  934. /* ============================================================ */
  935. /* insert separator line, variable number, separated by 1 blank */
  936. /* ============================================================ */
  937. void sep_line(FILE *pf,                 /* output file pointer           */
  938.               char c,                   /* separator char                */
  939.               unsigned short int size,...)  /* length(s)                 */
  940. {
  941.   char buf[80];                         /* work buffer                   */
  942.   unsigned short int k,ll;              /* offset and size               */
  943.   va_list mk;                           /* argument marker               */
  944.  
  945.   memset(buf, c, 79);                   /* whole line buffer             */
  946.   buf[79] = '\0';                       /* end of string                 */
  947.   va_start(mk, size);                   /* start variable arg processing */
  948.   k = size;                             /* take first                    */
  949.   while (k < 80) {                      /* all parts of line             */
  950.     buf[k++] = ' ';                     /* replace sepline char by space */
  951.     ll = va_arg(mk, unsigned short int);  /* next argument               */
  952.     if (ll > 0)                         /* more in list                  */
  953.       k += ll;                          /* add to offset                 */
  954.     else {
  955.       buf[k] = '\0';                    /* end of string                 */
  956.       break;                            /* escape from while-loop        */
  957.       }
  958.     }
  959.   fprintf(pf, "%-s\n", buf);            /* output                        */
  960.   va_end(mk);                           /* end variable argument list    */
  961.   }
  962.  
  963. /* ================================== */
  964. /* some marketing below every report! */
  965. /* ================================== */
  966. void signature(FILE *pf,
  967.                char *now)
  968. {
  969.   static char *mode[] = {"DOS","OS/2"};
  970.  
  971.   fprintf(pf,"\n\n  ");
  972.   sep_line(pf, '═', 73, 0);
  973.   fprintf(pf,"   This list was created with %s %c.%c%c (%s-bits)  -  by %s\n"
  974.              "                  on %s under %s %d.%d\n  ",
  975.                 PROGNAME,VERSION,SUBVERS,SUFFIX,
  976. #ifndef __32BIT__
  977.                 "16",
  978. #else
  979.                 "32",
  980. #endif
  981.                 AUTHOR, now,
  982.                 mode[_osmode & 1],
  983.                 _osmajor/( (_osmode) ? 10 : 1),
  984.                 _osminor/10);
  985.   sep_line(pf, '═', 73, 0);
  986.   fprintf(pf, "\n");                    /* extra space                   */
  987.   }
  988.  
  989. /* ================ */
  990. /* HELP information */
  991. /* ================ */
  992. void show_help(void)
  993. {
  994.   static char *help[] = {
  995.    "Syntax:  [drive:][path]DOWNSORT  [@filespec]  [/H | /Q | /V]\n\n",
  996.    "@filespec   - '@' followed by filespec of Downsort's parameter file\n",
  997.    "/H          - Display this HELP-screen\n",
  998.    "/Q          - Suppress all progress messages\n",
  999.    "/V          - Display extensive progress messages\n",
  1000.    "───────────\n",
  1001.    "Read documentation and sample configuration file "
  1002.      "for details and defaults.\n",
  1003.    NULL};
  1004.  
  1005.   int i;
  1006.   for (i=0; help[i]; ++i)
  1007.     printf(help[i]);
  1008.   exit(1);
  1009.   }
  1010.  
  1011.