home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / cpgms.zip / DIRCMP.C < prev    next >
C/C++ Source or Header  |  1985-11-20  |  14KB  |  583 lines

  1.  
  2. #include "stdio.h"
  3.  
  4. /*
  5.  * dircmp.
  6.  *
  7.  *
  8.  * See below for more information.
  9.  *
  10.  */
  11.  
  12.  
  13. char    *documentation[] = {
  14. "dircmp compares two directories.  Execute by",
  15. "   dircmp [flags] path_a path_b",
  16. "",
  17. "Flags are single characters preceeded by '-':",
  18. "   -a      Print filenames of files in path_a and not in path_b",
  19. "   -b      Print filenames of files in path_b and not in path_a",
  20. "   -m      Print filenames of files in both path_a and path_b",
  21. "                 (path_a is output.  See -p flag below)",
  22. "   -p      Prefix file name with its entire path",
  23. "   -f      Prefix a %1 and suffix a %2 to each line",
  24. "   -e      Extended compare includes date/time in comparison",
  25. "",
  26. "The path_a and path_b are similar to the parameters to DOS COPY command).",
  27.  
  28. "",
  29. "The output is sent to STDOUT and, therefore, may be redirected.",
  30. "",
  31. 0 };
  32.  
  33.  
  34.  
  35. #define DeSmet 1
  36. #define MSDOS 1
  37.  
  38. #ifndef stdin
  39. #define stdin STDIN
  40. #define stdout STDOUT
  41. #define stderr STDERR
  42. #endif
  43.  
  44. #define LMAX    512        /* max number of entries per directory */
  45.  
  46. int    mflag = 0;
  47. int    fflag = 0;
  48. int    pflag = 0;
  49. int    eflag = 0;
  50.  
  51. int    debug    =    0;       /* Set for debug code      */
  52.  
  53.  
  54. struct dirent {
  55.     char dname[13];            /* name + ext + EOS */
  56.     unsigned date;
  57.     unsigned time;
  58.     char fill_eos;            /* give strcmp an end of string */
  59. };
  60.  
  61. int        numd1;                /* number of entries in d1 */
  62. struct dirent d1[LMAX];        /* area for storing directory a */
  63. int        numd2;                /* number of entries in d2 */
  64. struct dirent d2[LMAX];        /* area for storing directory b */
  65.  
  66. char endmark[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '.',
  67.                   0xff, 0xff, 0xff, 0x00};
  68.  
  69. struct dirent *dp[2];
  70. int *dn[2];
  71. char path_a[80], path_b[80], *paths[2];
  72. char mydta[80] = {0};
  73.  
  74.  
  75. /*******************************************************/
  76.  
  77. main(argc, argv)
  78. char *argv[];
  79. int argc;
  80. {
  81.     register int c;
  82.     register char *p;
  83.     register int i, j;
  84.     int nfile, nmatch, loaddir(), outname(), usage(), error();
  85.     int help(), qsort(), strcmp();
  86.     long match;
  87.  
  88.     dp[0] = d1;        /* allow indirect addressing of d1 and d2 */
  89.     dp[1] = d2;
  90.      
  91.     dn[0] = &numd1;        /* allow indirect addressing of numd1 and numd2 */
  92.     dn[1] = &numd2;
  93.      
  94.     paths[0] = path_a;    /* indirect for path maintenance */
  95.     paths[1] = path_b;
  96.      
  97.    if (argc <= 1)
  98.       usage("No arguments");
  99.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  100.       help(documentation);
  101.       return;
  102.       }
  103.    nfile = argc-1;
  104.    for (i=1; i < argc; ++i) {
  105.       p = argv[i];
  106.       if (*p == '-') {
  107.      ++p;
  108.      while (c = *p++) {
  109.         switch(tolower(c)) {
  110.  
  111.         case '?':
  112.            help(documentation);
  113.            break;
  114.  
  115.         case 'a':
  116.            mflag = -1;
  117.            break;
  118.  
  119.         case 'd':
  120.            ++debug;
  121.            break;
  122.  
  123.         case 'b':
  124.            mflag = 1;
  125.            break;
  126.  
  127.         case 'p':
  128.            ++pflag;
  129.            break;
  130.  
  131.         case 'f':
  132.            ++fflag;
  133.            break;
  134.  
  135.         case 'm':
  136.            mflag = 0;
  137.            break;
  138.  
  139.         case 'e':
  140.            ++eflag;
  141.            break;
  142.  
  143.         default:
  144.            usage("Unknown flag");
  145.         }
  146.      }
  147.      argv[i] = 0;
  148.      --nfile;
  149.       } 
  150.    }
  151.    if (nfile < 2)
  152.       usage("Two paths are required.  dircmp -? for instructions.");
  153.    else if (nfile > 2)
  154.          usage("Only two paths are allowed.  dircmp -? for instructions.");
  155.    else {
  156.          j = 0;
  157.       for (i=1; i < argc; i++) 
  158.                   if (p = argv[i]) 
  159.             if (loaddir(p, j++))    /* j is index to paths[] and dp[] */
  160.                 error("Cannot load directory for ", p);
  161.     }
  162.  
  163.     qsort(d1, numd1, sizeof(d1[0]), strcmp);    /* sort path_a array */
  164.     qsort(d2, numd2, sizeof(d1[0]), strcmp);    /* sort path_b array */
  165.  
  166.     i = j = 0;
  167.     while ((strcmp(&d1[i].dname, endmark))
  168.          || (strcmp(&d2[j].dname, endmark))) {
  169.  
  170.     /*  check for match of names and (optionally) date/time */
  171.  
  172.         nmatch = match = strcmp(&d1[i].dname, &d2[j].dname);
  173.         if ((!match) && eflag)
  174.             if (!(match = ((long)d2[j].date - (long)d1[i].date)))
  175.                 match = ((long)d2[j].time - (long)d1[i].time);
  176.  
  177.     /* normalize match variable to -1, 0, or +1  */
  178.         if (match)
  179.             if (match < 0)
  180.                 match = -1;
  181.             else
  182.                 match = 1;
  183.  
  184.         if (match == mflag)
  185.             outname(i, j, match);    /* output whichever is lower */
  186.         if (nmatch <= 0)            /* pop path_a entry */
  187.             i++;                    /*  on name match basis only */
  188.         if (nmatch >= 0)            /* pop path_b entry */
  189.             j++;
  190.     }
  191.  
  192.     if (mflag < 0)                /* finish path a entries */
  193.         while (strcmp(&d1[i].dname, endmark)) {
  194.             outname(i, j, match);    /* output one record */
  195.             i++;
  196.         }
  197.     else if (mflag > 0)                /* finish path b entries */
  198.         while (strcmp(&d2[j].dname, endmark)) {
  199.             outname(i, j, match);    /* output one record */
  200.             j++;
  201.         }
  202. }
  203.  
  204. /*******************************************************/
  205.  
  206. int loaddir(path, indndx)
  207. char *path, indndx;
  208. int indndx;
  209. {
  210.     char *fexpnd(), *getret;
  211.     int i, strcpy(), *cnvttime, *cnvtdate;
  212.  
  213.     i = 0;
  214.     cnvttime = &mydta[22];
  215.     cnvtdate = &mydta[24];
  216.     while (getret = fexpnd(path, paths[indndx])) {
  217.         strcpy(dp[indndx] + i, getret);    /* copy file.ext to next bucket */
  218.         if (i > (LMAX-1))
  219.             error("Table overflow for path ",path);
  220.         if (eflag) {
  221.             (*(dp[indndx]+i)).time = *cnvttime;
  222.             (*(dp[indndx]+i)).date = *cnvtdate;
  223.         }
  224.         (*(dp[indndx]+i)).fill_eos = '\0';
  225.         i++;
  226.     }
  227.     strcpy(dp[indndx] + i, endmark);
  228.     *dn[indndx] = i + 1;
  229.     return FALSE;
  230.     
  231. }
  232.  
  233. /*******************************************************/
  234.  
  235. outname(d1ndx, d2ndx, match)
  236. int d1ndx, d2ndx, match;
  237. {
  238.     int bldname();
  239.  
  240.     if (match <= 0)
  241.         bldname(&d1[d1ndx].dname, 0);
  242.     else
  243.         bldname(&d2[d2ndx].dname, 1);
  244. }
  245.  
  246.  
  247. /*******************************************************/
  248.  
  249. bldname(fname, pathndx)
  250. char *fname;
  251. int pathndx;
  252. {
  253.     int puts();
  254.  
  255.     if (fflag)
  256.         puts("%1 ");    /* output leading %1, if requested */
  257.     if (pflag)
  258.         puts(paths[pathndx]);    /* output path prefix, if requested */
  259.     puts(fname);        /* output file name */
  260.     if (fflag)
  261.         puts(" %2");    /* output trailing %2, if requested */
  262.     puts("\n");            /* and add end of line */
  263.      
  264. }
  265.  
  266.  
  267. /*******************************************************/
  268.  
  269. usage(s)
  270. char    *s;
  271. {
  272.    puts("?DIRCMP-E-");
  273.    puts(s);
  274.    puts("\n");
  275.    puts("Usage: dircmp [-abmfp].   dircmp ? for help\n");
  276.    exit(1);
  277. }
  278.  
  279.  
  280.  
  281. /*******************************************************/
  282.  
  283. /*
  284.     fexpnd returns a pointer to the next filename.ext which
  285.     matches the first parameter (str) or a zero is no file 
  286.     matches that parameter.  The given parameter is processed 
  287.     to generate a normalized search string and a normalized 
  288.     path prefix.  The prefix is returned in the area pointed to 
  289.     by the second parameter.  This string may be concatenated 
  290.     with the returned value to give a fully-qualified dataset 
  291.     name for open, rename, erase, etc.
  292.  
  293.     You should continue calling fexpnd with the same first 
  294.     parameter until it returns a zero, then pass it a new first 
  295.     parameter.  This can be used to expand a list of ambiguous 
  296.     filenames on a parameter list.   */
  297.  
  298.  
  299. char fxs[80] = {0};
  300. char fxsrch[80] = {0};
  301. char fxpref[80] = {0};
  302.  
  303. char *fexpnd(str, path)
  304. char *str, *path;
  305. {
  306.     int getret, fixpath(), strcpy(), dirnxt(), dirfst();
  307.     int savdta(), restdta();
  308.     char *rindex();
  309.  
  310.     savdta();        /* save old dta in an unknown location */
  311.     setdta(&mydta); /* make MS-DOS use my dta */
  312.     if (strcmp(str,fxs) == 0)
  313.         getret = dirnxt();        /* MS-DOS 2.0+ get next function */
  314.     else {
  315.         strcpy(fxs,str);            /* for detection of a new name */
  316.         if (!fixpath(str, fxsrch, fxpref))
  317.             return (0);
  318.         strcpy(path, fxpref);        /* give path to caller */
  319.         getret = dirfst(fxsrch);    /* MS-DOS 2.0+ get first function */
  320.     }
  321.  
  322.     restdta();        /* restore old dta */
  323.     if (getret == 0)  {
  324.         return(&mydta[30]);        /* return filename.ext  */
  325.     }
  326.     else
  327.         return 0;
  328. }
  329.  
  330. int dtads, dtadx;
  331.  
  332. savdta()
  333. {
  334. #asm
  335.     PUSH    AX
  336.     PUSH    ES
  337.     PUSH    BX
  338.     MOV        AH,2fh
  339.     INT        21h
  340.     MOV        WORD dtads_,ES
  341.     MOV        WORD dtadx_,BX
  342.     POP        BX
  343.     POP        ES
  344.     POP        AX
  345. #
  346. }
  347.  
  348. restdta()
  349. {
  350. #asm
  351.     PUSH    DS
  352.     PUSH    DX
  353.     PUSH    AX
  354.     MOV        DX,WORD dtads_
  355.     MOV        DS,DX
  356.     MOV        DX,WORD dtadx_
  357.     MOV        AH,1AH
  358.     INT        21h
  359.     POP        AX
  360.     POP        DX
  361.     POP        DS
  362. #
  363. }
  364.  
  365. char *dtap;
  366.  
  367. setdta(newdta)
  368. char *newdta;
  369. {
  370.     dtap = newdta;
  371. #asm
  372.     PUSH    DX
  373.     PUSH    AX
  374.     MOV        DX,WORD dtap_
  375.     MOV        AH,1AH
  376.     INT        21h
  377.     POP        AX
  378.     POP        DX
  379. #
  380. }
  381.  
  382. char *dirfnp;
  383.  
  384. dirfst(s)
  385. char *s;
  386. {
  387.     dirfnp = s;
  388. #asm
  389.     PUSH    DX
  390.     PUSH    CX
  391.     MOV        DX,WORD dirfnp_
  392.     MOV        AH,4eh
  393.     INT        21h
  394.     JC    D56X1
  395.     MOV    AX,0
  396. D56X1:    NOP
  397.     POP    CX
  398.     POP        DX
  399. #
  400. }
  401.  
  402. dirnxt()
  403. {
  404. #asm
  405.     MOV        AH,4fh
  406.     INT        21h
  407.     JC    D56X2
  408.     MOV    AX,0
  409. D56X2:    NOP
  410. #
  411. }
  412.  
  413.  
  414. /* ================================================================ */
  415. /*
  416.     From Dr. Dobbs #108 October 1985.   */
  417.  
  418. /* fixpath() processes a DOS pathname for two different uses.
  419.     The input path, *ip, is presumably a command operand like the 
  420.     first operand of DIR.  One output, the search path *sp, is 
  421.     the input possibly augmented with "*.*" or "\*.*" so that it 
  422.     will work reliably with DOS functions 4E/$f.  The other output
  423.     is a lead-in path, *lip, which can be prefixed to the simple 
  424.     filename.ext returned by fuctions 4E/4F to make a complete path 
  425.     for opening or erasing a file.
  426.  
  427.     The function returns 1 if it is successful, but 0 if the 
  428.     input path can't be processed and should not be used.
  429.  
  430.     Some input paths can be processed here yet be invalid or 
  431.     useless.  The search path produced from such an input wwill
  432.     cause an error return from function 4E (search first match). */
  433. /* ================================================================ */
  434.  
  435. int fixpath(ip, sp, lip)
  436. register char     *ip,    /* input path */
  437.                 *sp,    /* the search path */
  438.                 *lip;    /* the lead-in or prefix path */
  439. {
  440.     char *cp;            /* work pointer for scanning paths */
  441.     char attr;            /* attribute for chgattr */
  442.     int ret, strlen(), strcpy(), strcmp(), strcat(), chgattr();
  443.  
  444. /* ================================================================ */
  445. /*    Pick off 4 special cases:
  446.     (1)    the NULL string, which we take to mean "*.*"
  447.     (2)    the simple "d:" reference to a drive, which we also take
  448.         to mean "d:*.*"
  449.     (3)    the root-dir reference "d:\" which is mishandled by 
  450.         function 0x4300 of both dDOS 2.1 and 3.0.
  451.     (4)    any reference that ends in "\"
  452.  
  453.     In all cases, the input path is ok as a lead-in, but it needs
  454.     the global qualifier *.* added for searching.
  455.  
  456. /* ================================================================ */
  457.  
  458.     if ((strlen(ip) == 0)                /* null string */
  459.         || (strcmp(ip+1, ":") == 0)        /* d: only */
  460.         || (ip[strlen(ip)-1] == '\\'))    /* ends in backslash */
  461.         {
  462.             strcpy(lip, ip);            /* input is ok as prefix */
  463.             strcpy(sp, ip);
  464.             strcat(sp, "*.*");            /* add *.* for search */
  465.             return (1);
  466.         }
  467.  
  468. /* ================================================================ */
  469. /*    Ok, we have a non-null string not ending in \ and not a lone
  470.     drive letter.  Four possibilities remain:
  471.     (1)    an explicit file reference, b:\mydir\mydat
  472.     (2)    an explicit directory reference, \mydir
  473.     (3)    an ambiguous file reference, *.* or b:\mydir\*.dat
  474.     (4)    an unknown name, a:noway or b:\mydir\nonesuch.dat
  475.  
  476.     We can separate this with fair reliability using DOS function
  477.     0x4300, get attributes from path.
  478.  
  479.  
  480. /* ================================================================ */
  481.  
  482.     attr = 0x00;                        /* output area for get command */
  483.     ret = chgattr('G', ip, &attr);        /* get attributes for this path */
  484.  
  485.     if (ret == 0x0002) 
  486.  
  487.     /*   the path is valid, in that all directories and drives
  488.         named in it are valid, but the final name is unknown.  No
  489.         files will be found in a search, so quit now.   */
  490.  
  491.         return (0);
  492.  
  493.     if ((ret == 0x0003) 
  494.         || ((ret == 0) && ((attr & 0x0010) == 0))) {
  495.  
  496.     /* Error 3, path not found, could mean total junk or a 
  497.         misspelt directory name, but most likely it just means
  498.         the path ends in an ambiguous name.  If there's an error
  499.         the initial search (function 4Eh) will fail.
  500.  
  501.         With no error and reg cx not showing the directory 
  502.         attribute flags 0010, we have a normal, unambiguous file
  503.         pathname like "b:\mydir\mydat" or just "myprog.com".
  504.  
  505.         In either case the search path is the whole input
  506.         path.  The lead-in is that shorn of whatever follows the 
  507.         rightmost \ or :, whichever comes last -- or just a null
  508.         string if there is no \ or :.        */
  509.  
  510.         strcpy(sp, ip);            /* working copy of ip in sp */
  511.         cp = sp + strlen(sp) -1;
  512.         for (; cp > sp; --cp)
  513.             if (('\\' == *cp) || (':' == *cp)) 
  514.                 break;
  515.         if (cp > sp)
  516.             ++cp;                /* retain colon or slash */
  517.         *cp = '\0';                /* make a null string */
  518.         strcpy(lip, sp);
  519.         strcpy(sp, ip);            /* whole input for search */
  520.         return (1);
  521.     }
  522.  
  523.     if ((ret == 0) && (attr & 0x0010)) {
  524.  
  525.     /* No error and the directory attribute returned in cx
  526.         shows an unambiguous path that happpens to end in the 
  527.         name of a directory, for instance "..\..\bin" or
  528.         "b:\mydir".  Applying the rules of DIR or COPY, we
  529.         have to treat these as global refs to all files named in 
  530.         the directory.  The search path is the input with 
  531.         "\*.*" tacked onto it.  The lead-in path is just the 
  532.         input plus a backslash.            */
  533.  
  534.         strcpy(sp, ip);
  535.         strcpy(lip, ip);
  536.         strcat(sp, "\\*.*");
  537.         strcat(lip, "\\");
  538.         return (1);
  539.     }
  540.  
  541.     else {
  542.         /* unexpected events make me nervous, so give up */
  543.         return (0);
  544.     }
  545. }
  546.  
  547. int rax, rcx;
  548. char *rdx;
  549.  
  550. int chgattr(getset, path, attr)
  551. char getset, *path, *attr;
  552. {
  553.     if (getset == 'S') {
  554.         rcx = *attr;
  555.         rax = 0x4301;
  556.     }
  557.     else
  558.         rax = 0x4300;
  559.     
  560.     rdx = path;
  561.  
  562. #asm
  563.     PUSH    DX
  564.     PUSH    CX
  565.     PUSH    AX
  566.     MOV        DX,WORD rdx_
  567.     MOV        AX,WORD rax_
  568.     MOV        CX,WORD rcx_
  569.     INT        21H
  570.     MOV        WORD rax_,AX
  571.     JC        D56X9
  572.     MOV        WORD rax_,0
  573. D56X9:    NOP
  574.     MOV        WORD rcx_,CX
  575.     POP        AX
  576.     POP        CX
  577.     POP        DX
  578. #
  579.     if (rax == 0)
  580.         *attr = rcx;
  581.     return(rax);
  582. }        
  583.