home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1989
/
03
/
dlugosz.lst
next >
Wrap
File List
|
1989-02-10
|
14KB
|
535 lines
_EXTENDED DIRECTORY SEARCHES USING C++_
by John M. Dlugosz
[LISTING ONE]
// DIRPATH.HPP Copyright 1988 by John M. Dlugosz
struct dirpath {
char drive[3];
char path[65];
char base[9];
char ext[5];
void operator<< (char const*name); //throw a name into the structure
void operator>> (char *name); //extract name from structure
dirpath(char const* name) {*this << name;}
dirpath(char const* d, char const* p, char const* b, char const* e)
{strcpy(drive,d);strcpy(path,p);strcpy(base,b);strcpy(ext,e);}
dirpath() {}
};
[LISTING TWO]
// example using dirpath
// by John M. Dlugosz
#include <stream.hpp>
#include <string.h>
#include <dirpath.hpp>
char infile[67], outfile[67];
void checkfiles (char const* name)
{ /* find input and output filenames */
dirpath d (name);
if (*d.ext == '\0') //supply default extension for input file
strcpy(d.exe, ".C"); //notice dot in included
d >> infile;
strcpy (d.ext, ".OBJ");
d >> outfile;
}
[LISTING THREE]
/*****************************************************
File: DIRPATH.CPP
Copyright 1988 by John M. Dlugosz, all rights reserved
parse and combine file names
*****************************************************/
// you must define NULL for your compiler. Most have a way of checking the
// model being compiled under.
#ifdef LPTR
#define NULL 0L
#else
#define NULL 0
#endif
#include <string.h>
#include "dirpath.hpp"
static void copy (char *dest, char const* source, char const* const limit, int count)
{
while (count-- && source <= limit) *dest++ = *source++;
*dest= '\0';
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
void dirpath::operator<< (char const* name)
{
//takes a pathname and splits it up into its components.
//any or all of the components may be missing.
//first, locate DRIVE letter
if (strlen(name) >= 2 && name[1]==':') {
drive[0]= name[0];
drive[1]= ':';
drive[2]= '\0';
name += 2; }
else drive[0]= '\0'; //no drive found
//locate last slash and dot
char *p;
char *last_slash= NULL, *dot= NULL;
for (p= name; *p; p++)
if (*p == '\\' || *p == '/') last_slash= p;
else if (*p == '.') dot= p;
// p now points to the \0 at the end of the string
if (last_slash) { //path exists. copy up to and including the last slash
copy (path, name, last_slash, 64);
name= last_slash+1;
}
else *path= '\0';
if (dot) { //break int base and ext
copy (base, name, dot-1, 8);
copy (ext, dot, p-1, 4);
}
else { //rest of string is base name, no ext.
copy (base, name, p-1, 8);
*ext= '\0';
}
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
void dirpath::operator>> (char *name)
{
//concatenate the parts together into a full name.
//the name parameter better be long enough.
if (*drive) {
*name++ = *drive;
*name++ = ':'; }
if (*path) {
strcpy (name, path);
name += strlen (name);
if (name[-1] != '/' && name[-1] != '\\')
*name++ = '\\';
}
strcpy (name, base);
if (*ext) {
if (*ext != '.') strcat (name,".");
strcat (name, ext);
}
}
[LISTING FOUR]
// DIRSCAN.HPP Copyright 1988 by John M. Dlugosz
/* these values for attribute parameter in constructor
are passed through to DOS's function 4e */
#define fa_READ_ONLY 0x01
#define fa_HIDDEN 0x02
#define fa_SYSTEM 0x04
#define fa_LABEL 0x08
#define fa_DIR 0x10
#define fa_ARCHIVE 0x20
class dir_data {
char reserved[21];
public:
unsigned char attribute;
unsigned time;
unsigned date;
unsigned long size;
char name[13];
};
class dir_scanner {
char **goodlist, **badlist;
int goodcount, badcount;
dir_data current_entry;
bool done;
bool firsttime;
void near buildlists (char *filespec);
bool near multi_match();
public:
dir_scanner (char *filespec, unsigned attribute=0);
~dir_scanner();
operator int(); //look up next entry, return 1. If no next, return 0.
dir_data& operator() () {return current_entry;} //fetch current entry
};
[LISTING FIVE]
/*****************************************************
File: DIRSCAN.CPP
Copyright 1988 by John M. Dlugosz, all rights reserved
implementation of class dir_scanner, which does
enhanced file matching in a directory list.
*****************************************************/
enum bool {FALSE,TRUE};
#define NULL 0L //define NULL properly for your compiler and model
#include <string.h>
#include "dirscan.hpp"
// these two function in assembly language (file SCANHELP.ASM)
extern int dirscan_findfirst (char const far *name, unsigned attribute, dir_data const far *dta);
extern int dirscan_findnext (dir_data const far *dta);
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
static void near dir_scanner::buildlists (char *pattern)
{
goodcount= badcount= 0;
for (char *p= pattern;;) { // pass 1 -- count them
while (*p == ' ' || *p == '\t') p++;
if (*p == '\0') break;
if (*p == ';') badcount++;
else goodcount++;
do p++; while (*p != ' ' && *p != '\t' && *p != ';' && *p != '+' && *p != '\0');
}
typedef char* POINTER; //I need a type name so I can use NEW
goodlist= new POINTER [goodcount]; // allocate arrays
badlist= new POINTER [badcount];
int gcount=0, bcount= 0;
/* I remember the location of the end of the substring in lastbreak. After
parsing the next string, I stick a '\0' here. I do this one string
behind because it will overwrite the '+' or ';' */
char *lastbreak= NULL; //NULL means none found yet
for (;;) { // pass 2 -- chop it up
while (*pattern == ' ' || *pattern == '\t') pattern++;
/* Embedded whitespace tolerated. You can even omit the '+' and seperate
names with spaces. But if you do have a '+' or ';', it must
immediately preceed the name */
if (*pattern == '\0') break; //end of the string
if (*pattern == ';') //add to BAD list
badlist[bcount++]= ++pattern;
else { //add to GOOD list
if (*pattern == '+') pattern++;
goodlist[gcount++]= pattern; }
if (lastbreak) *lastbreak= '\0';
while (! (*pattern==' ' || *pattern=='\t' || *pattern=='\0' ||
*pattern==';' || *pattern=='+')) pattern++;
lastbreak= pattern;
}
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
dir_scanner::dir_scanner (char *filespec, unsigned attribute)
{
char dir[67], *lastslash= NULL, *p;
int max= 63;
firsttime= TRUE;
// seperate the path information from the search specification
for (p= filespec; *p; p++)
if (*p == ':' || *p == '/' || *p == '\\') lastslash= p;
// notice that both \ and / are acceptable.
p= dir;
if (lastslash) //copy directory information
// if it is too long, it is truncated. You might want to check and
// return an error if this happens (max will be -1 after the loop)
while (max-- && filespec <= lastslash) *p++= *filespec++;
// if it is too long, it is truncated. You might want to check and
// return an error if this happens (max will be -1 after the loop)
strcpy (p, "*.*");
buildlists (lastslash ? lastslash+1 : filespec);
/* the first name is read differently then the others. So read the first
here, and set firsttime so the advance and test function will not
advance. */
if (0 != dirscan_findfirst(dir, attribute, ¤t_entry)) done= TRUE;
else {
done= FALSE;
return; }
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
dir_scanner::~dir_scanner()
{
delete goodlist;
delete badlist;
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
static bool near matchname (char const* pattern, char const* name)
{
while (*pattern) {
if ((*pattern == *name) || (*pattern == '?' && *name)) {
pattern++;
name++;
}
else if (*pattern == '*') {
for (;;) {
if (matchname (pattern+1, name)) return TRUE;
if (*name) name++;
else return FALSE;
}
}
else return FALSE;
}
return *name == '\0';
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
static char * near normalize (char const* pattern)
{
static char buf[16];
if (*pattern == '.') {
// if no basename, insert '*'. i.e. ".EXE" => "*.EXE"
buf[0]= '*';
strcpy (buf+1, pattern);
return buf; }
else if (!strchr(pattern,'.')) {
// if no dot, extension of '*'. i.e. "FOO" => "FOO.*"
strcpy (buf, pattern);
strcat (buf, ".*");
return buf; }
return pattern;
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
static void near check_for_dot (char* name)
{ /* if name does not contain a dot, add one to the end */
while (*name)
if (*name == '.') return;
else name++;
*name++ = '.';
*name= '\0';
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
static bool near dir_scanner::multi_match()
{
/* a match occurs if the name does not match anything on the badlist,
and does match something on the goodlist */
check_for_dot (current_entry.name);
int count;
for (count= 0; count < badcount; count++)
if (matchname (normalize(badlist[count]), current_entry.name)) return FALSE;
for (count= 0; count < goodcount; count++)
if (matchname (normalize(goodlist[count]), current_entry.name)) return TRUE;
return FALSE;
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
dir_scanner::operator int()
{ /* this is the advance and test function */
while (!done) {
if (!firsttime) {
if (0 != dirscan_findnext (¤t_entry)) {
done= TRUE;
return FALSE; }
}
else firsttime= FALSE;
if (multi_match()) return TRUE;
}
return FALSE;
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
[LISTING SIX]
;SCANHELP.ASM Copyright 1988 by John M. DLugosz, all rights reserved
;extern int dirscan_findfirst (
; char const far *name, unsigned attribute, dir_data const far *dta);
;extern int dirscan_findnext (dir_data const far *dta);
.MODEL LARGE,C
; to adapt to any memory model, make sure the RET is the right kind, and
; that the parameters are accessed properly on the stack frame. In
; MASM 5.1, just change the preceeding line. LARGE and SMALL versions
; are enogth to service any model, because the data size does not matter
; thanks to the prototypes.
.CODE
dirscan_findfirst proc uses DS, fname:DWORD, attribute:WORD, dta:DWORD
lds DX,dta
mov AH,1ah
int 21h ;set disk transfer area
lds DX,fname
mov CX,attribute
mov AH,4eh
int 21h ;find it.
jc error
xor AX,AX ;return 0 for ok.
error: ;error code is already in AX
ret
dirscan_findfirst endp
dirscan_findnext proc uses DS, dta:DWORD
lds DX,dta
mov AH,1ah
int 21h ;set disk transfer area
mov AH,4fh
int 21h ;find it.
jc error
xor AX,AX
error: ;error code already in AX
ret
dirscan_findnext endp
END
[LISTING SEVEN]
// Version A
static bool near matchname (char const* pattern, char const* name)
{
if (*pattern == '?')
return (*name != '\0' && matchname (pattern+1, name+1);
else if (*pattern == '*') {
while (*++name)
if (matchname (pattern+1, name)) return TRUE;
return FALSE
}
else if (*pattern == *name) {
if (*pattern == '\0') return TRUE;
else matchname (pattern+1, name+1);
}
else return FALSE;
}
// Version B - apply tail-end recursion elimination
static bool near matchname (char const* pattern, char const* name)
{
restart:
if (*pattern == '?') {
if (!*name) return FALSE;
pattern++;
name++;
goto restart;
}
else if (*pattern == '*') {
while (*++name)
if (matchname (pattern+1, name)) return TRUE;
return FALSE
}
else if (*pattern != *name) return FALSE
else if (*pattern == '\0') return TRUE;
else {
pattern++;
name++;
goto restart;
}
}
// Version C - get rid of goto's
static bool near matchname (char const* pattern, char const* name)
{
while (*pattern) {
if (*pattern == *name) || (*pattern == '?' && *name) {
pattern++;
name++;
}
else if (*pattern == '*') {
for (;;) {
if (matchname (pattern+1, name) return TRUE;
if (*name) name++;
else return FALSE;
}
}
else return FALSE;
}
return *name == '\0';
}
[LISTING EIGHT]
/*****************************************************
File: MATCH.CPP
Copyright 1988 by John M. Dlugosz, all rights reserved
demonstration of dir_scanner
*****************************************************/
enum bool {FALSE, TRUE};
#include <stream.hpp>
#include "dirscan.hpp"
#include <ctype.h> //toupper() needed
/* The nl macro might already be in stream.hpp. If it is not,
you might want to add it. Otherwise, define it here. */
#define nl << "\n"
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
ostream& operator<< (ostream& o, dir_data& d)
{
/* let output streams handle directory entries. This just prints the
name, but you might want to enhance it to prinmt the date, size, etc. */
o << d.name;
return o;
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
void upcase (char* s)
{ /* convert string to all caps */
while (*s) {
*s= toupper (*s);
s++;
}
}
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
main (int argc, char* argv[])
{
char s[80];
dir_data f;
if (argc < 2) { //prompt user
cout << "enter search request: ";
cin >> s;
cout nl;
}
else strcpy (s, argv[1]); //use command line if present
// the dir_scanner is assumes parameter is ALL CAPS
upcase (s);
dir_scanner d (s); //notice second parameter is missing, so default is used.
int number= 0; //count how many found
while (d) { //advance and test function called here
f= d(); //fetch function called
number++; //count it
cout << f nl; //display it
}
cout << number << " files found." nl;
}