home *** CD-ROM | disk | FTP | other *** search
/ Beijing Paradise BBS Backup / PARADISE.ISO / software / BBSDOORW / DWNSRS56.ZIP / DOWNSRV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-21  |  32.7 KB  |  907 lines

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