home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / FILE / FIND.ZIP / FINDFILE.C next >
Encoding:
C/C++ Source or Header  |  1990-10-24  |  29.5 KB  |  1,139 lines

  1. static char *copyright = "findfile.c: Copyright (c) 1990. Stephen J Doyle";
  2. /*
  3.  * findfile: Copyright (c) 1990. Stephen J Doyle.
  4.  *
  5.  * This software remains the property of the author Stephen J Doyle.
  6.  * This software is supplied 'as is' and without express or implied
  7.  * warranty.
  8.  * Permission is granted to copy, distribute or modify this program
  9.  * provided this copyright message is included with the modified program
  10.  * and any modifications are commented or highlighted in the program text.
  11.  *
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <dir.h>
  17. #include <dos.h>
  18. #include <assert.h>
  19. #include <ctype.h>
  20.  
  21. typedef int int16;
  22. typedef long int32;
  23. typedef unsigned int uint16;
  24.  
  25. static char *spec="*.*";
  26. static char *prune=NULL;
  27. static char **prunedirs;
  28. static int nprunes=0;
  29. static char *root=NULL;
  30. static int32 minsize=0, maxsize=0;
  31. static unsigned int spacing;
  32. static int attrib=0;
  33. static int extsort=0;
  34. static int ls=0;
  35. static char *LS=NULL;
  36. static int LSlen;
  37. static int dirsep;
  38. static int switchar;
  39. static int reversesort=0;
  40.  
  41. /*
  42.  * Directory entry information recorded for sorting
  43.  */
  44. struct dent {
  45.     char name[13];
  46.     char attrib;
  47.     int32 size;
  48.     uint16 date, time;
  49. };
  50.  
  51. typedef struct dent *Dentptr;
  52.  
  53. /*
  54.  * Simple boolean type
  55.  */
  56. typedef enum {
  57.     True = 1,
  58.     False = 0
  59. } Boolean;
  60.  
  61. /*
  62.  * Comparison types
  63.  */
  64. typedef enum {
  65.     None,
  66.     GreaterThan,
  67.     LessThan,
  68.     Equal,
  69.     GreaterOrEqual,
  70.     LessThanOrEqual,
  71.     UnEqual
  72. } Comparison;
  73.  
  74. /*
  75.  * Date/time held as individual and composite values
  76.  * with comaprison/masking details for checking time/date
  77.  * of file against user defined value
  78.  */
  79. struct mytime {
  80.     Comparison comparison;
  81.     uint16 year;
  82.     unsigned char
  83.     month,
  84.     day,
  85.     hour,
  86.     mins,
  87.     sec;
  88.     uint16 date, time;
  89.     uint16 datemask, timemask;
  90. } DateTime = {None};
  91.  
  92. typedef struct mytime Mytime, *MytimePtr;
  93.  
  94. /*
  95.  * Allocation multiple when allocating directory entries, this avoids
  96.  * too frequent calls to realloc
  97.  */
  98. #define ALLOCSIZE 32
  99.  
  100. /*
  101.  * In the interests of internationalization, country specific data
  102.  */
  103. struct country countrydata;
  104. char dtsep, tmsep;
  105. typedef enum {
  106.     DateAmerica = 0,
  107.     DateEurope = 1,
  108.     DateJapan = 2
  109. } DateFormat;
  110.  
  111. DateFormat dateformat;
  112.  
  113. /*
  114.  * Check that the program keeps proper track of all requests to
  115.  * allocate, adjust or free memory.
  116.  */
  117. int32 mallocspace=0;
  118.  
  119. void *mymalloc(size_t size){
  120.  
  121.     void *t=(void *)malloc(size+sizeof(size_t));
  122.     if (!t){
  123.     fprintf(stderr, "Out of memory in malloc\n");
  124.     abort();
  125.     } else {
  126.     *((size_t *)t)=size;
  127.     mallocspace+=size;
  128.     t=(void *)(((char *)t)+sizeof(size_t));
  129.     }
  130.     return(t);
  131. }
  132.  
  133. void *myrealloc(void *ptr, size_t newsize){
  134.     void *t=(void *)(((char *)ptr)-sizeof(size_t));
  135.  
  136.     mallocspace-= *((size_t *)t);
  137.  
  138.     t=(void *)realloc(t, newsize);
  139.  
  140.     if (!t){
  141.     fprintf(stderr, "Out of memory in realloc\n");
  142.     abort();
  143.     } else {
  144.     *((size_t *)t)=newsize;
  145.     mallocspace+=newsize;
  146.     t=(void *)(((char *)t)+sizeof(size_t));
  147.     }
  148.     return(t);
  149. }
  150.  
  151. void myfree(void *ptr){
  152.     void *t=(void *)(((char *)ptr)-sizeof(size_t));
  153.  
  154.     mallocspace-= *((size_t *)t);
  155.     free(t);
  156. }
  157.  
  158. /*
  159.  * Check the time stamp of a file against the user defined value
  160.  */
  161. Boolean TimeMatch(uint16 date, uint16 time){
  162.     Boolean result;
  163.     date&=DateTime.datemask;
  164.     time&=DateTime.timemask;
  165.     switch(DateTime.comparison){
  166.     case None:
  167.         result=True;
  168.         break;
  169.     case Equal:
  170.         result=((date==DateTime.date)&&
  171.             (time==DateTime.time))?True:False;
  172.         break;
  173.     case UnEqual:
  174.         result=((date!=DateTime.date)||
  175.             (time!=DateTime.time))?True:False;
  176.         break;
  177.     case LessThan:
  178.         result=((date<DateTime.date)||
  179.             ((date==DateTime.date)&&
  180.              (time<DateTime.time)))?True:False;
  181.         break;
  182.     case LessThanOrEqual:
  183.         result=((date<DateTime.date)||
  184.             ((date==DateTime.date)&&
  185.              (time<=DateTime.time)))?True:False;
  186.         break;
  187.     case GreaterThan:
  188.         result=((date>DateTime.date)||
  189.             ((date==DateTime.date)&&
  190.              (time>DateTime.time)))?True:False;
  191.         break;
  192.     case GreaterOrEqual:
  193.         result=((date>DateTime.date)||
  194.             ((date==DateTime.date)&&
  195.              (time>=DateTime.time)))?True:False;
  196.         break;
  197.     default:
  198.         fprintf(stderr, "Invalid time comparison case. Internal error\n");
  199.         abort();
  200.         break;
  201.     }
  202.     return(result);
  203. }
  204.  
  205. /*
  206.  * Check a filename against a given specification (which may contain * or ?
  207.  * wildcards in any position) to see if the name matches the specification.
  208.  */
  209. Boolean matchspec(char *spec, char *name, int len){
  210.     Boolean res;
  211.     int i;
  212.     if (spec[0]=='\0'){
  213.     res=(name[0]=='\0')?True:False;
  214.     } else if (spec[0]=='?') {
  215.     res = ((len>0) && (matchspec(&spec[1], &name[1], len-1)==True))
  216.         ?True:False;
  217.     } else if (spec[0]=='*'){
  218.     if ((len==0) || (spec[1]=='\0')) res=True;
  219.     else {
  220.         for (i=len-1; i>=0; i--) {
  221.             if (matchspec(&spec[1], &name[i], len-i)==True)
  222.             break;
  223.         }
  224.         res=(i>=0)?True:False;
  225.     }
  226.     } else {
  227.     res= ((spec[0]==name[0])&&(len>0)&&(matchspec(&spec[1], &name[1], len-1)==True))?True:False;
  228.     }
  229.     return(res);
  230. }
  231.  
  232. /*
  233.  * Search out the files of the current directory to try and display
  234.  * those matching the users search criterion.
  235.  */
  236. void displaydir(char *parent)
  237. {
  238.     struct ffblk entry;
  239.     int i, lt, j, finished, len, sl;
  240.     char *path;
  241.     char extra[40]={'\0'};
  242.     int extraptr=0;
  243.     Dentptr dents;
  244.     Dentptr *sortarr=NULL, swaptmp;
  245.     int dentcount=0;
  246.  
  247.     dents=(Dentptr)mymalloc(sizeof(struct dent)*ALLOCSIZE);
  248.  
  249.     /*
  250.      * From the parent directory workk out the file specification used in
  251.      * the file search
  252.      */
  253.     if (parent==NULL) {
  254.     len=0;
  255.         path=(char *)mymalloc(len+13);
  256.     } else {
  257.     int addsep;
  258.     len=strlen(parent);
  259.     addsep=(parent[len-1]!=dirsep);
  260.     if (addsep) ++len;
  261.         path=(char *)mymalloc(len+13);
  262.     strcpy(path, parent);
  263.     if (addsep) path[len-1]=dirsep;
  264.     }
  265.     sprintf(&path[len], "*.*");
  266.     strlwr(path);
  267.  
  268.     /*
  269.      * Make a first pass to find all none directory files matching the
  270.      * specification
  271.      */
  272.     if (findfirst(path, &entry, attrib & ~FA_DIREC)==0){
  273.     do {
  274.         strlwr(entry.ff_name);
  275.  
  276.         /*
  277.          * Check file name against spec, size against min and max values
  278.          * and time stamp
  279.          */
  280.         if ((matchspec(spec, entry.ff_name, strlen(entry.ff_name))==True)&&
  281.         ((minsize==0)||
  282.          (entry.ff_fsize>=minsize))&&
  283.         ((maxsize==0)||
  284.          (entry.ff_fsize<=maxsize))&&
  285.          TimeMatch(entry.ff_fdate, entry.ff_ftime)){
  286.         struct dent *dp= &dents[dentcount];
  287.  
  288.         /*
  289.          * Store the file details
  290.          */
  291.             dp->attrib=entry.ff_attrib;
  292.         dp->size=entry.ff_fsize;
  293.         dp->date=entry.ff_fdate;
  294.         dp->time=entry.ff_ftime;
  295.             strcpy(dp->name, entry.ff_name);
  296.  
  297.         /*
  298.          * If used up all current block of entries, append a further
  299.          * block
  300.          */
  301.             if (((++dentcount)%ALLOCSIZE)==0){
  302.             dents=(struct dent *)myrealloc(dents,
  303.                    (dentcount+ALLOCSIZE)*sizeof(struct dent));
  304.         }
  305.         }
  306.     } while (findnext(&entry)==0);
  307.     }
  308.  
  309.     /*
  310.      * Now make another search to find the directories
  311.      */
  312.     sprintf(&path[len], "*.*");
  313.     if (findfirst(path, &entry, FA_DIREC)==0){
  314.         do {
  315.  
  316.         /*
  317.          * Discard entries which are not directories or are the
  318.          * entries for the current/parent directories or are
  319.          * directories that the user wants to exclude
  320.          */
  321.         if ((entry.ff_attrib&FA_DIREC)&&
  322.         (strcmp(entry.ff_name, ".")!=0)&&
  323.         (strcmp(entry.ff_name, "..")!=0)){
  324.         int discard=0;
  325.         int elen=strlen(entry.ff_name);
  326.         strlwr(entry.ff_name);
  327.         if (prune){
  328.             for (i=0; i<nprunes; i++){
  329.             if (matchspec(prunedirs[i], entry.ff_name, elen)){
  330.                 discard=1;
  331.                 break;
  332.                 }
  333.             }
  334.         }
  335.         if (!discard){
  336.             struct dent *dp= &dents[dentcount];
  337.  
  338.             /*
  339.              * Store the directory details
  340.              */
  341.                 dp->attrib=entry.ff_attrib;
  342.             dp->size=entry.ff_fsize;
  343.             dp->date=entry.ff_fdate;
  344.             dp->time=entry.ff_ftime;
  345.  
  346.             strcpy(dp->name, entry.ff_name);
  347.  
  348.             /*
  349.              * If used up all current block of entries, append a further
  350.              * block
  351.              */
  352.             if (((++dentcount)%ALLOCSIZE)==0){
  353.                 dents=(struct dent *)myrealloc(dents,
  354.                 (dentcount+ALLOCSIZE)*sizeof(struct dent));
  355.             }
  356.         }
  357.         }
  358.     } while (findnext(&entry)==0);
  359.     }
  360.  
  361.     path[len]='\0';
  362.  
  363.     /*
  364.      * If anything found, proceed
  365.      */
  366.     if (dentcount){
  367.  
  368.     /*
  369.      * The filename table now has to be sorted. To enable this to
  370.      * be reasonably fast a binary chop algorithm is used to swap
  371.      * pointers.
  372.      */
  373.         sortarr=(Dentptr *)mymalloc(dentcount*sizeof(Dentptr));
  374.         for (j=0; j<dentcount; j++)
  375.         sortarr[j]= &dents[j];
  376.  
  377.     spacing=((dentcount)/2);
  378.     for (i=(sizeof(spacing)*8-1); i>0; i--)
  379.         if (spacing&(1<<i)) break;
  380.  
  381.     for (spacing=1<<i; spacing>0; spacing/=2)
  382.         for (j=0; j<(dentcount-spacing); j++)
  383.         if (extsort) {
  384.             int m, n, s1, s2;
  385.  
  386.             /*
  387.              * Extension precedence sort, check file extensions
  388.              * first then the file name portion
  389.              */
  390.             for (m=0; sortarr[j]->name[m]!='\0'&&
  391.                   sortarr[j]->name[m]!='.'; m++);
  392.             for (n=0; sortarr[j+spacing]->name[n]!='\0'&&
  393.                   sortarr[j+spacing]->name[n]!='.'; n++);
  394.             if (((s1=strcmp(&sortarr[j]->name[m],
  395.                 &sortarr[j+spacing]->name[n]))>0)||
  396.             ((s1==0)&&
  397.              ((s2=strncmp(sortarr[j]->name, sortarr[j+spacing]->name,
  398.                 min(m, n)))>0)||
  399.              ((s2==0)&&(m>n)))){
  400.                 swaptmp=sortarr[j];
  401.                 sortarr[j]=sortarr[j+spacing];
  402.                 sortarr[j+spacing]=swaptmp;
  403.             }
  404.         } else {
  405.  
  406.             /*
  407.              * Sort by file name then extension, this is much simpler
  408.              */
  409.             if (strcmp(sortarr[j]->name, sortarr[j+spacing]->name)>0){
  410.                 swaptmp=sortarr[j];
  411.                 sortarr[j]=sortarr[j+spacing];
  412.                 sortarr[j+spacing]=swaptmp;
  413.             }
  414.                 }
  415.  
  416.         /*
  417.      * Now, display the entries
  418.      */
  419.     if (reversesort) j=dentcount-1;
  420.     else j=0;
  421.     finished=0;
  422.     while (!finished){
  423.  
  424.         /*
  425.          * Display if directories are selected or the file is any other
  426.          * file type
  427.          */
  428.         if ((!(sortarr[j]->attrib&FA_DIREC))||
  429.         ((attrib&FA_DIREC)&&
  430.          TimeMatch(sortarr[j]->date, sortarr[j]->time))){
  431.  
  432.         /*
  433.          * The LS option takes precedence over the ls option.
  434.          * For the ls option the file attributes and size (in K)
  435.          * are displayed
  436.          */
  437.              if (ls & !LS) {
  438.             char att=sortarr[j]->attrib;
  439.             sprintf(extra, " %cr%c%c%c%c %5ldK",
  440.                 (att&FA_DIREC)?'d':'-',
  441.                 (att&FA_RDONLY)?'-':'w',
  442.                 (att&FA_HIDDEN)?'h':'-',
  443.                 (att&FA_SYSTEM)?'s':'-',
  444.                 (att&FA_ARCH)?'m':'-',
  445.             sortarr[j]->size/1000L);
  446.  
  447.         /*
  448.          * For the LS option, work through the LS argument in turn
  449.          * so that fields are displayed in the order the user
  450.          * specifies
  451.          */
  452.             } else if (LS){
  453.             int16 date=sortarr[j]->date;
  454.             int16 time=sortarr[j]->time;
  455.             int32 size=sortarr[j]->size;
  456.             char att=sortarr[j]->attrib;
  457.             extraptr=0;
  458.             for (i=0; i<LSlen; i++){
  459.             switch(LS[i]){
  460.  
  461.                 /*
  462.                  * Attribute display
  463.                  */
  464.                 case 'a':
  465.                         sprintf(&extra[extraptr], " %cr%c%c%c%c",
  466.                          (att&FA_DIREC)?'d':'-',
  467.                         (att&FA_RDONLY)?'-':'w',
  468.                         (att&FA_HIDDEN)?'h':'-',
  469.                         (att&FA_SYSTEM)?'s':'-',
  470.                         (att&FA_ARCH)?'m':'-');
  471.                 break;
  472.  
  473.                 /*
  474.                  * File size in M bytes
  475.                  */
  476.                 case 'm':
  477.                 sprintf(&extra[extraptr], " %5ldM", size/1000000L);
  478.                 break;
  479.  
  480.                 /*
  481.                  * File size in K bytes
  482.                  */
  483.                 case 'k':
  484.                 sprintf(&extra[extraptr], " %5ldK", size/1000L);
  485.                 break;
  486.  
  487.                 /*
  488.                  * File size in bytes
  489.                  */
  490.                 case 's':
  491.                 sprintf(&extra[extraptr], " %6ld", size);
  492.                 break;
  493.  
  494.                 /*
  495.                  * File time
  496.                  */
  497.                 case 't':
  498.                                 sprintf(&extra[extraptr],
  499.                     " %02d%c%02d%c%02d",
  500.                     (time>>11)&0x1f,
  501.                     tmsep,
  502.                     (time>>5)&0x3f,
  503.                     tmsep,
  504.                     (time&0x1f)<<1);
  505.                 break;
  506.  
  507.                 /*
  508.                  * Date (display in format defined
  509.                  * by country specific data).
  510.                  */
  511.                 case 'd':
  512.                 switch(dateformat){
  513.                     case DateAmerica:
  514.                     sprintf(&extra[extraptr],
  515.                         " %02d%c%02d%c%04d",
  516.                                               (date>>5)&0xf,
  517.                                               dtsep,
  518.                           (date&0x1f),
  519.                                               dtsep,
  520.                           ((date>>9)&0x7f)+1980);
  521.                         break;
  522.                 case DateEurope:
  523.                     sprintf(&extra[extraptr],
  524.                         " %02d%c%02d%c%04d",
  525.                                               (date&0x1f),
  526.                                               dtsep,
  527.                           (date>>5)&0xf,
  528.                                               dtsep,
  529.                           ((date>>9)&0x7f)+1980);
  530.                     break;
  531.                 case DateJapan:
  532.                     sprintf(&extra[extraptr],
  533.                         " %04d%c%02d%c%02d",
  534.                                               ((date>>9)&0x7f)+1980,
  535.                                               dtsep,
  536.                           (date>>5)&0xf,
  537.                                               dtsep,
  538.                           (date&0x1f));
  539.                         break;
  540.                     }
  541.                 break;
  542.             }
  543.             extraptr=strlen(extra);
  544.             }
  545.                 }
  546.  
  547.             printf("%s%s%s\n", path, sortarr[j]->name, extra);
  548.             }
  549.  
  550.         /*
  551.          * If directory, now descend into the directory to see what
  552.          * is in it
  553.          */
  554.             if (sortarr[j]->attrib&FA_DIREC){
  555.         sprintf(&path[len], "%s", sortarr[j]->name);
  556.         displaydir(path);
  557.         path[len]='\0';
  558.         }
  559.  
  560.          /*
  561.          * End of loop increment/decrement
  562.          */
  563.         if (reversesort){
  564.         if (--j<0) finished=1;
  565.         } else {
  566.         if (++j>=dentcount) finished=1;
  567.         }
  568.     }
  569.     myfree(sortarr);
  570.     }
  571.  
  572.     myfree(dents);
  573.     myfree(path);
  574. }
  575.  
  576. /*
  577.  * Display how the program is used
  578.  */
  579. void usage(name, error)
  580. char *name;
  581. char *error;
  582. {
  583.     struct date date;
  584.     struct time time;
  585.     if (error) {
  586.     fprintf(stderr, "Illegal argument %s\n", error);
  587.     }
  588.     fprintf(stderr, "Usage: %s [[directory] [%coption ...] [%cp] ...]\n",
  589.     strlwr(name), switchar, switchar);
  590.     fprintf(stderr, "\tFile finding utility. Copyright (c) 1990 Stephen J Doyle.\n");
  591.     fprintf(stderr, "\t                                         (steved@inmos.co.uk)\n");
  592.     fprintf(stderr, "Options:\n");
  593.     fprintf(stderr, "\t%cn (%cname)       - Wildcard file specification to find\n",
  594.     switchar, switchar);
  595.     fprintf(stderr, "\t%cx (%cxd)         - Comma separated list of subdirectories not searched\n",
  596.     switchar, switchar);
  597.     fprintf(stderr, "\t%cs (%csort)       - Sort by file extension before name\n",
  598.     switchar, switchar);
  599.     fprintf(stderr, "\t%cr (%crev)        - Reverse order of sort\n",
  600.     switchar, switchar);
  601.     fprintf(stderr, "\t%ct (%ctype) ftype - Also search for files with 'ftype' attributes\n",
  602.     switchar, switchar);
  603.     fprintf(stderr, "\t\t[ftype can contain h(idden), s(ystem), d(irectory) or * (all)]\n");
  604.     fprintf(stderr, "\t%cd (%cdate) datex - Search for files matching date/time expression\n",
  605.     switchar, switchar);
  606.     fprintf(stderr, "\t%cmin size[km]    - Search for files >= size[km] bytes\n",
  607.     switchar);
  608.     fprintf(stderr, "\t%cmax size[km]    - Search for files <= size[km] bytes\n",
  609.     switchar);
  610.     fprintf(stderr, "\t%cp (%cprint)      - Print all files matching search criteria\n",
  611.     switchar, switchar);
  612.     fprintf(stderr, "\t%cls              - Display file attributes and size (in K bytes)\n",
  613.     switchar);
  614.     fprintf(stderr, "\t%cLS listoptions  - Display file details given by listoptions\n",
  615.     switchar);
  616.     fprintf(stderr, "\t\t[listoptions can contain d(ate), t(ime), s(ize in bytes),\n");
  617.     fprintf(stderr, "\t\t k (size in Kbytes), m (size in Mbytes) or a(ttributes)]\n");
  618.     fprintf(stderr, "\t%c? (%chelp)       - Display (this) help text\n",
  619.     switchar, switchar);
  620.  
  621.     fprintf(stderr, "\toptions can be switched off using a trailing %c e.g. %csort%c\n\n",
  622.     switchar, switchar, switchar);
  623.     if (isatty(fileno(stdin))){
  624.     fprintf(stderr, "- More -");
  625.     fflush(stderr);
  626.     while (!kbhit());
  627.     getch();
  628.     fprintf(stderr, "\n\n");
  629.     }
  630.     fprintf(stderr, "\tOptions can be used multiple times overriding previous settings.\n");
  631.     fprintf(stderr, "\tMultiple use of %cprint causes multiple file finding operations.\n\n",
  632.     switchar);
  633.     fprintf(stderr, "A file specification (using %cname) or directory specification (using %cxd)\n",
  634.     switchar, switchar);
  635.     fprintf(stderr, "may contain * or ? wildcard characters in any position.\n\n");
  636.     fprintf(stderr, "A date expression (datex) is a string of the form [comp]datetime\n");
  637.     fprintf(stderr, "\tcomp specifies a comparison operation on the file date/time\n");
  638.     fprintf(stderr, "\tcomp is one of: !=, ne,        not equal\n");
  639.     fprintf(stderr, "\t                ge,            greater or equal(default)\n");
  640.     fprintf(stderr, "\t                le,            less than or equal\n");
  641.     fprintf(stderr, "\t                gt, g,         greater than\n");
  642.     fprintf(stderr, "\t                lt, l,         less than\n");
  643.     fprintf(stderr, "\t                ==, eq, =, e.  equal\n");
  644.     fprintf(stderr, "\n\tdatetime is of the form ");
  645.     getdate(&date);
  646.     gettime(&time);
  647.     switch(dateformat){
  648.     case DateAmerica:
  649.         fprintf(stderr, "%02d%c%02d%c%04d%c%02d%c%02d%c%02d",
  650.                             date.da_mon, dtsep,
  651.                 date.da_day, dtsep,
  652.                 date.da_year, dtsep,
  653.                 time.ti_hour, tmsep,
  654.                 time.ti_min, tmsep,
  655.                 time.ti_sec);
  656.             break;
  657.     case DateEurope:
  658.         fprintf(stderr, "%02d%c%02d%c%04d%c%02d%c%02d%c%02d",
  659.                             date.da_day, dtsep,
  660.                 date.da_mon, dtsep,
  661.                 date.da_year, dtsep,
  662.                 time.ti_hour, tmsep,
  663.                 time.ti_min, tmsep,
  664.                 time.ti_sec);
  665.         break;
  666.     case DateJapan:
  667.         fprintf(stderr, "%04d%c%02d%c%02d%c%02d%c%02d%c%02d",
  668.                             date.da_year, dtsep,
  669.                 date.da_mon, dtsep,
  670.                 date.da_day, dtsep,
  671.                 time.ti_hour, tmsep,
  672.                 time.ti_min, tmsep,
  673.                 time.ti_sec);
  674.         break;
  675.     }
  676.     fprintf(stderr, "\n\n\tIf no value is given between field seperators the value\n");
  677.     fprintf(stderr, "\tdefaults to the equivalent current date/time value.\n");
  678.     fprintf(stderr, "\t'*' for a field matches any value.\n\n");
  679.     exit(2);
  680. }
  681.  
  682. /*
  683.  * Comparison strings used in date/time specification by the user
  684.  */
  685. struct {
  686.     char *type;
  687.     Comparison match;
  688. } comparisons[]={
  689.    {"!=", UnEqual},
  690.    {"=!", UnEqual},
  691.    {"ne", UnEqual},
  692.    {"ge", GreaterOrEqual},
  693.    {"le", LessThanOrEqual},
  694.    {"gt", GreaterThan},
  695.    {"lt", LessThan},
  696.    {"==", Equal},
  697.    {"eq", Equal},
  698.    {"=",  Equal},
  699.    {"e",  Equal},
  700.    {"g",  GreaterThan},
  701.    {"l",  LessThan},
  702.    {"!",  None},
  703.    {"",   GreaterOrEqual},
  704. };
  705.  
  706. /*
  707.  * Details for date validation
  708.  */
  709. int monlen []={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  710. #define isleap(y) ((y)%4==0 && (y) % 100 !=0 || (y) %400 ==0)
  711.  
  712. /*
  713.  * Parse an individual field
  714.  */
  715. void checkf(char **p, int *d, int *got, int sep){
  716.     char *cp= *p;
  717.     if (*cp=='*'){
  718.     cp++;
  719.     if (*cp==sep)
  720.         cp++;
  721.     } else {
  722.         if (isdigit(*cp)) {
  723.             *d=(*cp++)-'0';
  724.             while (isdigit(*cp))
  725.             *d=10* *d+(*cp++)-'0';
  726.             *got=1;
  727.         }
  728.         if (*cp==sep){
  729.             *got=1;
  730.            cp++;
  731.         }
  732.     }
  733.     *p=cp;
  734. }
  735.  
  736. /*
  737.  * Convert a string specified by the user into a date/time structure
  738.  */
  739. Boolean CvtStringToTime(char *string, MytimePtr rval){
  740.     char *wptr=string;
  741.     Comparison ctype=GreaterOrEqual;
  742.     int i;
  743.     struct date dateblk;
  744.     struct time timeblk;
  745.     int d1, d2, d3, h, m, s;
  746.     int d1got=0, d2got=0, d3got=0, hgot=0,
  747.     mgot=0, sgot=0, yeargot=0, mongot=0, daygot=0;
  748.  
  749.     /*
  750.      * FIrst, establish the default values
  751.      */
  752.     getdate(&dateblk);
  753.     switch(dateformat){
  754.     case DateAmerica:
  755.         d1=dateblk.da_mon;
  756.         d2=dateblk.da_day;
  757.         d3=dateblk.da_year;
  758.         break;
  759.     case DateEurope:
  760.         d1=dateblk.da_day;
  761.         d2=dateblk.da_mon;
  762.         d3=dateblk.da_year;
  763.         break;
  764.     case DateJapan:
  765.         d1=dateblk.da_year;
  766.         d2=dateblk.da_mon;
  767.         d3=dateblk.da_day;
  768.         break;
  769.     }
  770.     gettime(&timeblk);
  771.     h=timeblk.ti_hour;
  772.     m=timeblk.ti_min;
  773.     s=timeblk.ti_sec;
  774.  
  775.     /*
  776.      * See if the user has selected one of the above comparisons
  777.      */
  778.     for (i=0; i<(sizeof(comparisons)/sizeof(comparisons[0])); i++){
  779.     if (strncmp(wptr, comparisons[i].type,
  780.              strlen(comparisons[i].type))==0){
  781.         ctype=comparisons[i].match;
  782.         wptr+=strlen(comparisons[i].type);
  783.         break;
  784.     }
  785.     }
  786.  
  787.     checkf (&wptr, &d1, &d1got, dtsep);
  788.     checkf (&wptr, &d2, &d2got, dtsep);
  789.     checkf (&wptr, &d3, &d3got, dtsep);
  790.     checkf (&wptr, &h, &hgot, tmsep);
  791.     checkf (&wptr, &m, &mgot, tmsep);
  792.     checkf (&wptr, &s, &sgot, tmsep);
  793.  
  794.     /*
  795.      * Store the hours, minutes, seconds then date according to the country
  796.      * specifier
  797.      */
  798.     rval->comparison=ctype;
  799.     rval->hour=h;
  800.     rval->mins=m;
  801.     rval->sec=s;
  802.     switch(dateformat){
  803.     case DateAmerica:
  804.         rval->month=d1;
  805.         rval->day=d2;
  806.         rval->year=d3;
  807.         mongot=d1got;
  808.         daygot=d2got;
  809.         yeargot=d3got;
  810.         break;
  811.     case DateEurope:
  812.         rval->day=d1;
  813.         rval->month=d2;
  814.         rval->year=d3;
  815.         daygot=d1got;
  816.         mongot=d2got;
  817.         yeargot=d3got;
  818.         break;
  819.     case DateJapan:
  820.         rval->year=d1;
  821.         rval->month=d2;
  822.         rval->day=d3;
  823.         yeargot=d1got;
  824.         mongot=d2got;
  825.         daygot=d3got;
  826.         break;
  827.     }
  828.  
  829.     /*
  830.      * If the user specified a short year form, add on the current century.
  831.      * Check the month for being valid and the day for being within the
  832.      * month (even if a leap year).
  833.      */
  834.     if (rval->year<100) rval->year+=((dateblk.da_year/100)*100);
  835.     if ((rval->year<1980)||(rval->year>(1980+128))) return(False);
  836.     if ((rval->month<1)||(rval->month>12)) return(False);
  837.     if (rval->month==2){
  838.     if (isleap(rval->year)) {
  839.         if (rval->day>29) return(False);
  840.     } else if (rval->day>monlen[rval->month]) return(False);
  841.     } else if (rval->day>monlen[rval->month]) return(False);
  842.  
  843.     /*
  844.      * Check the range of the hour, minute and second
  845.      */
  846.     if ((rval->hour<0)||(rval->hour>23)) return(False);
  847.     if ((rval->mins<0)||(rval->mins>59)) return(False);
  848.     if ((rval->sec<0)||(rval->sec>59)) return(False);
  849.  
  850.     /*
  851.      * Finally, compact the data into 16 bit integer format and
  852.      * store the mask value used to select user data
  853.      */
  854.     rval->year-=1980;
  855.     rval->datemask=(yeargot?0xfe00:0)|(mongot?0x1e0:0)|(daygot?0x1f:0);
  856.     rval->timemask=(hgot?0xf800:0)|(mgot?0x7e0:0)|(sgot?0x1f:0);
  857.     rval->date=((rval->year<<9)|(rval->month<<5)|(rval->day))&
  858.     rval->datemask;
  859.     rval->time=((rval->hour<<11)|(rval->mins<<5)|(rval->sec>>1))&
  860.     rval->timemask;
  861.     return(True);
  862. }
  863.  
  864. /*
  865.  * This is where it alll starts.
  866.  */
  867. main(int argc, char *argv[])
  868. {
  869.     int i, j, k;
  870.     int done=0;
  871.     int enable;
  872.     char *option;
  873.  
  874.     /*
  875.      * FInd out what the switch character is and set the switch and
  876.      * directory seperator for use in this program accordingly
  877.      */
  878.     _AH=0x37;
  879.     _AL=0;
  880.     __int__(0x21);
  881.     switchar=_DL;
  882.     if (switchar=='/')
  883.     dirsep='\\';
  884.     else
  885.     dirsep='/';
  886.  
  887.     /*
  888.      * Retrieve the country specific data
  889.      */
  890.     country(0, &countrydata);
  891.     dtsep=countrydata.co_dtsep[0];
  892.     tmsep=countrydata.co_tmsep[0];
  893.     dateformat=countrydata.co_date;
  894.  
  895.     /*
  896.      * Process options
  897.      */
  898.     for (i=1; i<argc; i++){
  899.     if (argv[i][0]==switchar){
  900.          option= &argv[i][1];
  901.          enable=argv[i][strlen(argv[i])-1]!=switchar;
  902.  
  903.          /*
  904.           * Search for a particular named file
  905.           */
  906.          if ((strcmp(option, "n")==0)||
  907.              (strcmp(option, "name")==0)){
  908.  
  909.         if (enable) {
  910.                 if (++i>=argc) usage(argv[0], argv[i-1]);
  911.                 spec=argv[i];
  912.         } else {
  913.             spec = "*.*";
  914.         }
  915.  
  916.         /*
  917.          * Terminate search at specified subdirectories
  918.          */
  919.             } else if ((strcmp(option, "x")==0)||
  920.                    (strcmp(option, "xd")==0)){
  921.  
  922.         if (prune!=NULL){
  923.                     for (k=0; k<nprunes; k++)
  924.                         myfree(prunedirs[k]);
  925.             myfree(prunedirs);
  926.         }
  927.  
  928.         if (enable) {
  929.                 if (++i>=argc) usage(argv[0], argv[i-1]);
  930.                 prune=argv[i];
  931.             strlwr(prune);
  932.             for (nprunes=1, k=0; prune[k]!='\0'; k++)
  933.             if (prune[k]==',') ++nprunes;
  934.             prunedirs=(char **)mymalloc(sizeof(char *)*nprunes);
  935.             for (nprunes=0, j=0, k=0; prune[k]!='\0'; k++)
  936.             if (prune[k]==','){
  937.                 prunedirs[nprunes]=(char *)mymalloc(k-j+1);
  938.                 strncpy(prunedirs[nprunes], &prune[j], k-j);
  939.                 prunedirs[nprunes++][k-j]='\0';
  940.                 j=k+1;
  941.             }
  942.             prunedirs[nprunes]=(char *)mymalloc(k-j+1);
  943.             strncpy(prunedirs[nprunes], &prune[j], k-j);
  944.             prunedirs[nprunes++][k-j]='\0';
  945.         } else {
  946.             prune = NULL;
  947.         }
  948.  
  949.         /*
  950.          * Display the program help
  951.          */
  952.             } else if ((strcmp(option, "?")==0)||
  953.                    (strcmp(option, "help")==0)){
  954.  
  955.         usage(argv[0], NULL);
  956.  
  957.         /*
  958.          * Sort by extension then file name rather than the
  959.          * other way round
  960.          */
  961.             } else if ((strcmp(option, "s")==0)||
  962.                    (strcmp(option, "sort")==0)){
  963.  
  964.         if (enable) {
  965.             extsort=1;
  966.         } else {
  967.             extsort=0;
  968.         }
  969.  
  970.         /*
  971.          * Sort in reverse order
  972.          */
  973.             } else if ((strcmp(option, "r")==0)||
  974.                (strcmp(option, "rev")==0)||
  975.                    (strcmp(option, "reverse")==0)){
  976.  
  977.         if (enable) {
  978.             reversesort=1;
  979.         } else {
  980.             reversesort=0;
  981.         }
  982.  
  983.         /*
  984.          * Display the additional file types hidden, system
  985.          * or directories
  986.          */
  987.             } else if ((strcmp(option, "t")==0)||
  988.                    (strcmp(option, "type")==0)){
  989.  
  990.         if (enable){
  991.             if (++i>=argc) usage(argv[0], argv[i-1]);
  992.             attrib=0;
  993.             for (j=0; j<strlen(argv[i]); j++)
  994.                 switch(argv[i][j]){
  995.                 case 'h': attrib|=FA_HIDDEN; break;
  996.                 case 's': attrib|=FA_SYSTEM; break;
  997.                 case 'd': attrib|=FA_DIREC; break;
  998.                 case '*': attrib|=FA_HIDDEN|FA_SYSTEM|FA_DIREC; break;
  999.                 default: usage(argv[0], argv[i]);
  1000.                 }
  1001.         } else {
  1002.             attrib=0;
  1003.         }
  1004.  
  1005.         /*
  1006.          * Specify a date search criterion
  1007.          */
  1008.             } else if ((strcmp(option, "d")==0)||
  1009.                    (strcmp(option, "date")==0)){
  1010.  
  1011.         if (enable){
  1012.             if (++i>=argc) usage(argv[0], argv[i-1]);
  1013.             if (!CvtStringToTime(argv[i], &DateTime))
  1014.                usage(argv[0], argv[i]);
  1015.         } else {
  1016.             DateTime.comparison=None;
  1017.         }
  1018.  
  1019.         /*
  1020.          * Specify a minimum file size to select
  1021.          */
  1022.             } else if (strcmp(option, "min")==0){
  1023.         char factor;
  1024.         if (enable){
  1025.                 if (++i>=argc) usage(argv[0], argv[i-1]);
  1026.             if (sscanf(argv[i], "%ld%c", &minsize, &factor)==0)
  1027.                 usage(argv[0], argv[i]);
  1028.             if ((factor=='k')||(factor=='K'))
  1029.                 minsize*=1024;
  1030.             else if ((factor=='m')||(factor=='M'))
  1031.                 minsize*=1024*1024;
  1032.         } else {
  1033.             minsize=0;
  1034.         }
  1035.  
  1036.         /*
  1037.          * Specify a maximum file size to select
  1038.          */
  1039.             } else if (strcmp(option, "max")==0){
  1040.         char factor;
  1041.         if (enable){
  1042.                 if (++i>=argc) usage(argv[0], argv[i-1]);
  1043.             if (sscanf(argv[i], "%ld%c", &maxsize, &factor)==0)
  1044.                 usage(argv[0], argv[i]);
  1045.             if ((factor=='k')||(factor=='K'))
  1046.                 maxsize*=1024;
  1047.             else if ((factor=='m')||(factor=='M'))
  1048.                 maxsize*=1024*1024;
  1049.         } else {
  1050.             maxsize=0;
  1051.         }
  1052.  
  1053.         /*
  1054.          * Do something
  1055.          */
  1056.         } else if ((strcmp(option, "p")==0)||
  1057.                (strcmp(option, "print")==0)){
  1058.  
  1059.             displaydir(root);
  1060.             done=1;
  1061.  
  1062.         /*
  1063.          * Enable the display of file attribute/size
  1064.          */
  1065.         } else if ((strcmp(option, "l")==0)||
  1066.                (strcmp(option, "ls")==0)){
  1067.         if (enable) {
  1068.                 ls=1;
  1069.         } else {
  1070.             ls=0;
  1071.         }
  1072.  
  1073.         /*
  1074.          * User selectable file details
  1075.          */
  1076.             } else if ((strcmp(option, "L")==0)||
  1077.                (strcmp(option, "LS")==0)){
  1078.         if (enable) {
  1079.                 if (++i>=argc) usage(argv[0], argv[i-1]);
  1080.             for (j=0; j<strlen(argv[i]); j++)
  1081.                 switch(argv[i][j]){
  1082.                 case 'd': case 't': case 's':
  1083.                 case 'k': case 'm': case 'a':
  1084.                 case 'D': case 'T': case 'S':
  1085.                 case 'K': case 'M': case 'A':
  1086.                     break;
  1087.                 default:
  1088.                     usage(argv[i], argv[i]);
  1089.                     break;
  1090.                 }
  1091.             strlwr(argv[i]);
  1092.             LS=argv[i];
  1093.             LSlen=strlen(argv[i]);
  1094.         } else {
  1095.             LS=NULL;
  1096.         }
  1097.  
  1098.         /*
  1099.          * Unrecognised option
  1100.          */
  1101.         } else {
  1102.         usage(argv[0], argv[i]);
  1103.         }
  1104.  
  1105.     /*
  1106.      * No option, this is the directory to start the search at
  1107.      */
  1108.         } else {
  1109.         root=argv[i];
  1110.     }
  1111.     }
  1112.  
  1113.     /*
  1114.      * If the program has completed without an attempt by the user at
  1115.      * doing something, display the help.
  1116.      */
  1117.     if (!done) usage(argv[0], NULL);
  1118.  
  1119.     /*
  1120.      * Last bit of freeing up necessary to enable correct memory
  1121.      * usage accounting to work
  1122.      */
  1123.     if (prune!=NULL){
  1124.         for (i=0; i<nprunes; i++)
  1125.             myfree(prunedirs[i]);
  1126.     myfree(prunedirs);
  1127.     }
  1128.  
  1129.     /*
  1130.      * Check the use of memory by the program
  1131.      */
  1132.     if (mallocspace){
  1133.     fprintf(stderr, "Memory leak. Internal failure\n");
  1134.     abort();
  1135.     }
  1136. }
  1137.  
  1138.  
  1139.