home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1989 / 03 / dlugosz.lst next >
File List  |  1989-02-10  |  14KB  |  535 lines

  1. _EXTENDED DIRECTORY SEARCHES USING C++_
  2. by John M. Dlugosz
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. // DIRPATH.HPP  Copyright 1988 by John M. Dlugosz
  8.  
  9. struct dirpath {
  10.    char drive[3];
  11.    char path[65];
  12.    char base[9];
  13.    char ext[5];
  14.    void operator<< (char const*name);  //throw a name into the structure
  15.    void operator>> (char *name);  //extract name from structure
  16.    dirpath(char const* name) {*this << name;}
  17.    dirpath(char const* d, char const* p, char const* b, char const* e)
  18.       {strcpy(drive,d);strcpy(path,p);strcpy(base,b);strcpy(ext,e);}
  19.    dirpath() {}
  20.    };
  21.  
  22.  
  23.  
  24. [LISTING TWO]
  25.  
  26. // example using dirpath
  27. // by John M. Dlugosz
  28.  
  29. #include <stream.hpp>
  30. #include <string.h>
  31. #include <dirpath.hpp>
  32.  
  33. char infile[67], outfile[67];
  34.  
  35. void checkfiles (char const* name)
  36. {     /* find input and output filenames */
  37. dirpath d (name);
  38. if (*d.ext == '\0') //supply default extension for input file
  39.    strcpy(d.exe, ".C");  //notice dot in included
  40. d >> infile;
  41. strcpy (d.ext, ".OBJ");
  42. d >> outfile;
  43. }
  44.  
  45.  
  46.  
  47. [LISTING THREE]
  48.  
  49. /*****************************************************
  50. File: DIRPATH.CPP
  51. Copyright 1988 by John M. Dlugosz, all rights reserved
  52.    parse and combine file names
  53. *****************************************************/
  54.  
  55. // you must define NULL for your compiler.  Most have a way of checking the
  56. // model being compiled under.
  57. #ifdef LPTR
  58. #define NULL 0L
  59. #else
  60. #define NULL 0
  61. #endif
  62.  
  63. #include <string.h>
  64. #include "dirpath.hpp"
  65.  
  66. static void copy (char *dest, char const* source, char const* const limit, int count)
  67. {
  68. while (count-- && source <= limit) *dest++ = *source++;
  69. *dest= '\0';
  70. }
  71.  
  72. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  73.  
  74. void dirpath::operator<< (char const* name)
  75. {
  76. //takes a pathname and splits it up into its components.
  77. //any or all of the components may be missing.
  78.  
  79. //first, locate DRIVE letter
  80. if (strlen(name) >= 2 && name[1]==':') {
  81.    drive[0]= name[0];
  82.    drive[1]= ':';
  83.    drive[2]= '\0';
  84.    name += 2;  }
  85. else drive[0]= '\0';   //no drive found
  86.  
  87. //locate last slash and dot
  88. char *p;
  89. char *last_slash= NULL, *dot= NULL;
  90. for (p= name; *p; p++)
  91.    if (*p == '\\' || *p == '/') last_slash= p;
  92.    else if (*p == '.') dot= p;
  93. // p now points to the \0 at the end of the string
  94. if (last_slash) { //path exists.  copy up to and including the last slash
  95.    copy (path, name, last_slash, 64);
  96.    name= last_slash+1;
  97.    }
  98. else *path= '\0';
  99. if (dot) {  //break int base and ext
  100.    copy (base, name, dot-1, 8);
  101.    copy (ext, dot, p-1, 4);
  102.    }
  103. else {  //rest of string is base name, no ext.
  104.    copy (base, name, p-1, 8);
  105.    *ext= '\0';
  106.    }
  107. }
  108.  
  109. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  110.  
  111. void dirpath::operator>> (char *name)
  112. {
  113. //concatenate the parts together into a full name.
  114. //the name parameter better be long enough.
  115. if (*drive) {
  116.    *name++ = *drive;
  117.    *name++ = ':';  }
  118. if (*path) {
  119.    strcpy (name, path);
  120.    name += strlen (name);
  121.    if (name[-1] != '/' && name[-1] != '\\')
  122.       *name++ = '\\';
  123.    }
  124. strcpy (name, base);
  125. if (*ext) {
  126.    if (*ext != '.') strcat (name,".");
  127.    strcat (name, ext);
  128.    }
  129. }
  130.  
  131.  
  132. [LISTING FOUR]
  133.  
  134.  
  135. // DIRSCAN.HPP  Copyright 1988 by John M. Dlugosz
  136.  
  137. /* these values for attribute parameter in constructor
  138.    are passed through to DOS's function 4e */
  139. #define fa_READ_ONLY 0x01
  140. #define fa_HIDDEN    0x02
  141. #define fa_SYSTEM    0x04
  142. #define fa_LABEL     0x08
  143. #define fa_DIR       0x10
  144. #define fa_ARCHIVE   0x20
  145.  
  146. class dir_data {
  147.    char reserved[21];
  148. public:
  149.    unsigned char attribute;
  150.    unsigned time;
  151.    unsigned date;
  152.    unsigned long size;
  153.    char name[13];
  154.    };
  155.  
  156. class dir_scanner {
  157.    char **goodlist, **badlist;
  158.    int goodcount, badcount;
  159.    dir_data current_entry;
  160.    bool done;
  161.    bool firsttime;
  162.    void near buildlists (char *filespec);
  163.    bool near multi_match();
  164. public:
  165.    dir_scanner (char *filespec, unsigned attribute=0);
  166.    ~dir_scanner();
  167.    operator int();  //look up next entry, return 1.  If no next, return 0.
  168.    dir_data& operator() () {return current_entry;}    //fetch current entry
  169.    };
  170.  
  171.  
  172. [LISTING FIVE]
  173.  
  174.  
  175. /*****************************************************
  176. File: DIRSCAN.CPP
  177. Copyright 1988 by John M. Dlugosz, all rights reserved
  178.    implementation of class dir_scanner, which does
  179.    enhanced file matching in a directory list.
  180. *****************************************************/
  181.  
  182. enum bool {FALSE,TRUE};
  183. #define NULL 0L  //define NULL properly for your compiler and model
  184. #include <string.h>
  185. #include "dirscan.hpp"
  186. // these two function in assembly language (file SCANHELP.ASM)
  187. extern int dirscan_findfirst (char const far *name, unsigned attribute, dir_data const far *dta);
  188. extern int dirscan_findnext (dir_data const far *dta);
  189.  
  190. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  191.  
  192. static void near dir_scanner::buildlists (char *pattern)
  193. {
  194. goodcount= badcount= 0;
  195. for (char *p= pattern;;) {  //  pass 1 -- count them
  196.    while (*p == ' ' || *p == '\t') p++;
  197.    if (*p == '\0') break;
  198.    if (*p == ';') badcount++;
  199.    else goodcount++;
  200.    do p++; while (*p != ' ' && *p != '\t' && *p != ';' && *p != '+' && *p != '\0');
  201.    }
  202. typedef char* POINTER;  //I need a type name so I can use NEW
  203. goodlist= new POINTER [goodcount];  // allocate arrays
  204. badlist= new POINTER [badcount];
  205. int gcount=0, bcount= 0;
  206. /* I remember the location of the end of the substring in lastbreak.  After
  207.    parsing the next string, I stick a '\0' here.  I do this one string
  208.    behind because it will overwrite the '+' or ';'  */
  209. char *lastbreak= NULL;  //NULL means none found yet
  210. for (;;) {  //  pass 2 -- chop it up
  211.    while (*pattern == ' ' || *pattern == '\t') pattern++;
  212.    /* Embedded whitespace tolerated.  You can even omit the '+' and seperate
  213.       names with spaces.  But if you do have a '+' or ';', it must
  214.       immediately preceed the name */
  215.    if (*pattern == '\0') break;  //end of the string
  216.    if (*pattern == ';')  //add to BAD list
  217.       badlist[bcount++]= ++pattern;
  218.    else {  //add to GOOD list
  219.       if (*pattern == '+') pattern++;
  220.       goodlist[gcount++]= pattern;  }
  221.    if (lastbreak) *lastbreak= '\0';
  222.    while (! (*pattern==' ' || *pattern=='\t' || *pattern=='\0' || 
  223.              *pattern==';' || *pattern=='+')) pattern++;
  224.    lastbreak= pattern;
  225.    }
  226. }
  227.  
  228. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  229.  
  230. dir_scanner::dir_scanner (char *filespec, unsigned attribute)
  231. {
  232. char dir[67], *lastslash= NULL, *p;
  233. int max= 63;
  234.  
  235. firsttime= TRUE;
  236. // seperate the path information from the search specification
  237. for (p= filespec; *p; p++)
  238.    if (*p == ':' || *p == '/' || *p == '\\') lastslash= p;
  239.    // notice that both \ and / are acceptable.
  240. p= dir;
  241. if (lastslash)   //copy directory information
  242.    // if it is too long, it is truncated.  You might want to check and
  243.    // return an error if this happens (max will be -1 after the loop)
  244.    while (max-- && filespec <= lastslash) *p++= *filespec++;
  245.    // if it is too long, it is truncated.  You might want to check and
  246.    // return an error if this happens (max will be -1 after the loop)
  247. strcpy (p, "*.*");
  248. buildlists (lastslash ? lastslash+1 : filespec);
  249. /* the first name is read differently then the others.  So read the first
  250.    here, and set firsttime so the advance and test function will not
  251.    advance. */
  252. if (0 != dirscan_findfirst(dir, attribute, ¤t_entry)) done= TRUE;
  253. else {
  254.    done= FALSE;
  255.    return;  }
  256. }
  257.  
  258. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  259.  
  260. dir_scanner::~dir_scanner()
  261. {
  262. delete goodlist;
  263. delete badlist;
  264. }
  265.  
  266. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  267.  
  268. static bool near matchname (char const* pattern, char const* name)
  269. {
  270. while (*pattern) {
  271.    if ((*pattern == *name) || (*pattern == '?' && *name)) {
  272.       pattern++;
  273.       name++;
  274.       }
  275.    else if (*pattern == '*') {
  276.       for (;;) {
  277.          if (matchname (pattern+1, name)) return TRUE;
  278.          if (*name) name++;
  279.          else return FALSE;
  280.          }
  281.       }
  282.    else return FALSE;
  283.    }
  284. return *name == '\0';
  285. }
  286.  
  287. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  288.  
  289. static char * near normalize (char const* pattern)
  290. {
  291. static char buf[16];
  292. if (*pattern == '.') {
  293.    // if no basename, insert '*'.  i.e.  ".EXE" => "*.EXE"
  294.    buf[0]= '*';
  295.    strcpy (buf+1, pattern);
  296.    return buf;  }
  297. else if (!strchr(pattern,'.')) {
  298.    // if no dot, extension of '*'.  i.e.  "FOO" => "FOO.*"
  299.    strcpy (buf, pattern);
  300.    strcat (buf, ".*");
  301.    return buf;  }
  302. return pattern;
  303. }
  304.  
  305. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  306.  
  307. static void near check_for_dot (char* name)
  308. {     /* if name does not contain a dot, add one to the end */
  309. while (*name)
  310.    if (*name == '.') return;
  311.    else name++;
  312. *name++ = '.';
  313. *name= '\0';
  314. }
  315.  
  316. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  317.  
  318. static bool near dir_scanner::multi_match()
  319. {
  320. /* a match occurs if the name does not match anything on the badlist,
  321.    and does match something on the goodlist */
  322. check_for_dot (current_entry.name);
  323. int count;
  324. for (count= 0;  count < badcount;  count++)
  325.    if (matchname (normalize(badlist[count]), current_entry.name)) return FALSE;
  326. for (count= 0;  count < goodcount;  count++)
  327.    if (matchname (normalize(goodlist[count]), current_entry.name)) return TRUE;
  328. return FALSE;
  329. }
  330.  
  331. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  332.  
  333. dir_scanner::operator int()
  334. {     /* this is the advance and test function */
  335. while (!done) {
  336.    if (!firsttime) {
  337.       if (0 != dirscan_findnext (¤t_entry)) {
  338.          done= TRUE;
  339.          return FALSE; }
  340.       }
  341.    else firsttime= FALSE;
  342.    if (multi_match()) return TRUE;
  343.    }
  344. return FALSE;
  345. }
  346.  
  347. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  348.  
  349.  
  350.  
  351. [LISTING SIX]
  352.  
  353. ;SCANHELP.ASM  Copyright 1988 by John M. DLugosz, all rights reserved
  354.  
  355. ;extern int dirscan_findfirst (
  356. ;        char const far *name, unsigned attribute, dir_data const far *dta);
  357. ;extern int dirscan_findnext (dir_data const far *dta);
  358.  
  359.      .MODEL LARGE,C
  360. ; to adapt to any memory model, make sure the RET is the right kind, and
  361. ; that the parameters are accessed properly on the stack frame.  In
  362. ; MASM 5.1, just change the preceeding line.  LARGE and SMALL versions
  363. ; are enogth to service any model, because the data size does not matter
  364. ; thanks to the prototypes.
  365.  
  366.  
  367.      .CODE
  368.  
  369. dirscan_findfirst proc uses DS, fname:DWORD, attribute:WORD, dta:DWORD
  370.    lds DX,dta
  371.    mov AH,1ah
  372.    int 21h    ;set disk transfer area
  373.    lds DX,fname
  374.    mov CX,attribute
  375.    mov AH,4eh
  376.    int 21h    ;find it.
  377.    jc error
  378.    xor AX,AX  ;return 0 for ok.
  379. error:  ;error code is already in AX
  380.    ret
  381. dirscan_findfirst endp
  382.  
  383.  
  384. dirscan_findnext proc uses DS, dta:DWORD
  385.    lds DX,dta
  386.    mov AH,1ah
  387.    int 21h    ;set disk transfer area
  388.    mov AH,4fh
  389.    int 21h    ;find it.
  390.    jc error
  391.    xor AX,AX
  392. error:  ;error code already in AX
  393.    ret
  394. dirscan_findnext endp
  395.  
  396.      END
  397.  
  398.  
  399. [LISTING SEVEN]
  400.  
  401.  
  402. // Version A
  403.  
  404. static bool near matchname (char const* pattern, char const* name)
  405. {
  406. if (*pattern == '?')
  407.    return (*name != '\0' && matchname (pattern+1, name+1);
  408. else if (*pattern == '*') {
  409.    while (*++name)
  410.       if (matchname (pattern+1, name)) return TRUE;
  411.    return FALSE
  412.    }
  413. else if (*pattern == *name) {
  414.    if (*pattern == '\0') return TRUE;
  415.    else matchname (pattern+1, name+1);
  416.    }
  417. else return FALSE;
  418. }
  419.  
  420.  
  421.  
  422. // Version B - apply tail-end recursion elimination
  423.  
  424. static bool near matchname (char const* pattern, char const* name)
  425. {
  426. restart:
  427. if (*pattern == '?') {
  428.    if (!*name) return FALSE;
  429.    pattern++;
  430.    name++;
  431.    goto restart;
  432.    }
  433. else if (*pattern == '*') {
  434.    while (*++name)
  435.       if (matchname (pattern+1, name)) return TRUE;
  436.    return FALSE
  437.    }
  438. else if (*pattern != *name) return FALSE
  439. else if (*pattern == '\0') return TRUE;
  440. else {
  441.    pattern++;
  442.    name++;
  443.    goto restart;
  444.    }
  445. }
  446.  
  447.  
  448. // Version C - get rid of goto's
  449.  
  450. static bool near matchname (char const* pattern, char const* name)
  451. {
  452. while (*pattern) {
  453.    if (*pattern == *name) || (*pattern == '?' && *name) {
  454.       pattern++;
  455.       name++;
  456.       }
  457.    else if (*pattern == '*') {
  458.       for (;;) {
  459.          if (matchname (pattern+1, name) return TRUE;
  460.          if (*name) name++;
  461.          else return FALSE;
  462.          }
  463.       }
  464.    else return FALSE;
  465.    }
  466. return *name == '\0';
  467. }
  468.  
  469.  
  470.  
  471. [LISTING EIGHT]
  472.  
  473. /*****************************************************
  474. File:  MATCH.CPP
  475. Copyright 1988 by John M. Dlugosz, all rights reserved
  476.    demonstration of dir_scanner
  477. *****************************************************/
  478.  
  479. enum bool {FALSE, TRUE};
  480. #include <stream.hpp>
  481. #include "dirscan.hpp"
  482. #include <ctype.h>  //toupper() needed
  483.  
  484. /* The nl macro might already be in stream.hpp.  If it is not,
  485.    you might want to add it.  Otherwise, define it here. */
  486. #define nl << "\n"
  487.  
  488. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  489.  
  490. ostream& operator<< (ostream& o, dir_data& d)
  491. {
  492. /* let output streams handle directory entries.  This just prints the
  493.    name, but you might want to enhance it to prinmt the date, size, etc. */
  494. o << d.name;
  495. return o;
  496. }
  497.  
  498. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  499.  
  500. void upcase (char* s)
  501. {     /* convert string to all caps */
  502. while (*s) {
  503.    *s= toupper (*s);
  504.    s++;
  505.    }
  506. }
  507.  
  508. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  509.  
  510.  
  511. main (int argc, char* argv[])
  512. {
  513. char s[80];
  514. dir_data f;
  515. if (argc < 2) {   //prompt user
  516.    cout << "enter search request: ";
  517.    cin >> s;
  518.    cout nl;
  519.    }
  520. else strcpy (s, argv[1]);  //use command line if present
  521. // the dir_scanner is assumes parameter is ALL CAPS
  522. upcase (s);
  523. dir_scanner d (s);  //notice second parameter is missing, so default is used.
  524. int number= 0;  //count how many found
  525. while (d) {     //advance and test function called here
  526.    f= d();      //fetch function called
  527.    number++;      //count it
  528.    cout << f nl;  //display it
  529.    }
  530. cout << number << " files found." nl;
  531. }
  532.  
  533.  
  534.  
  535.