home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
c_spec
/
execute
/
dir.c
< prev
next >
Wrap
C/C++ Source or Header
|
1986-02-20
|
16KB
|
598 lines
#include <stdio.h>
#include <ctype.h>
#include "getargs.h"
#include "mydos.h"
#include "dir.h"
/*----------------------------------------------------------------------+
* DIR.C: An MSDOS directory access function. |
* |
* (c) Copyright 1986, Allen I. Holub. All rights reserved. |
*----------------------------------------------------------------------+
* 11/22/85 Modified so that the total amount of disk space used |
* (ie. # of clusters) is put into the total, rather |
* than the file size. |
* 12/28/85 Modified so that the names: name1 name2 name10 name11 |
* will be sorted in that order, rather than in ASCII |
* (which would yield name1 name10 name11 name2) |
*----------------------------------------------------------------------+
*/
/* ROUND(n,u): if n is an even multiple of u, evaluate to n, else
* round n up to the next even multiple of u.
*/
#define ROUND(n,u) ( !((n) % (u)) ? (n) : (((n) / (u)) + 1) * (u))
#define BOLDFACE "\033[1m" /* Ansi esc sequence to turn bold face on */
#define ALL_OFF "\033[0m" /* " attributes off */
#define ATTRIBUTES (READONLY | DIRTY | SYSTEM | HIDDEN | SUBDIR)
#define iswhite(c) ((c) == ' ' || (c) == '\t')
/*----------------------------------------------------------------------*/
extern char *calloc (unsigned,unsigned); /* In standard library */
extern char *cptolower(char*,char*); /* In /src/tools/cptolow.c */
extern char *cpy (char*,char*); /* In /src/tools/cpy.c */
extern int mydos (REGS *); /* In /src/tools/dos.asm */
extern void gregs (REGS *); /* In /src/tools/dos.asm */
extern char *malloc (unsigned); /* In standard library */
extern char *next (char**,int,int); /* In /src/tools/next.c */
extern void ssort (char*,int,int,int(*)());/*in /src/tools/ssort.c */
extern int strcmp (char*, char*); /* in standard library */
/*----------------------------------------------------------------------*/
static unsigned Longfmt = 0; /* True if we're using long format. This
* has to be global for the comparison
* routine used for sroting to work.
*/
static unsigned Cluster_size; /* Number of bytes per cluster on
* requested disk.
*/
/*----------------------------------------------------------------------*/
/* Do a DOS system call using the dos() routine */
#define DOSCALL(id,regs) { regs.h.ah = id ; mydos( ®s ); }
/*----------------------------------------------------------------------*/
static int find_first( filespec, attributes, regp )
char *filespec ;
short attributes;
register REGS *regp;
{
/* Get directory information for the indicated file.
* Ambiguous file references are ok but you have to use
* find_next to get the rest of the file references.
* In this case, The regs structure used by find_first
* must be passed to find_next. 0 is returned on success,
* otherwise the DOS error code is returned.
*/
regp->h.ah = (char) FINDFIRST ;
regp->x.dx = (short) filespec ;
regp->x.cx = attributes ;
return (int)( (mydos(regp) & CARRY) ? regp->x.ax : 0 );
}
/*----------------------------------------------------------------------*/
static int find_next ( regp )
REGS *regp;
{
/* Get the next file in an ambiguous file reference. A
* call to this function must be preceded by a
* find_first call. The regp argument must be the
* same register image used by the find_first call.
* 0 is returned on success, otherwise the error code
* generated by DOS is returned.
*/
regp->h.ah = FINDNEXT ;
return (int)( (mydos(regp) & CARRY) ? regp->x.ax : 0 );
}
/*----------------------------------------------------------------------*/
int haswild(s)
register char *s;
{
/* Return true if s has a unix wild card in it. */
for( ; *s ; s++)
if( *s == '*' || *s == '?' )
return 1;
return 0;
}
/*----------------------------------------------------------------------*/
static int isrootdir( name )
register char *name;
{
/* return true if name is explicitly specifying the root
* directory (ie. is one of: d:/ d:\ / \ where
* 'd' can be any disk designator.
*/
if( *name && name[1] == ':' )
name += 2;
return( (*name == '\\' || *name == '/') && !name[1] );
}
/*----------------------------------------------------------------------*/
has_only( str, inclusion_set )
register char *str;
char *inclusion_set;
{
/* Return true only if every character in str is also in
* inclusion_set. True is returned if str is empty.
*/
register char *p;
for(; *str ; str++)
{
for( p = inclusion_set ; *p && *p != *str ; p++ )
;
if( !*p )
return 0;
}
return 1;
}
/*----------------------------------------------------------------------*/
static char *fixup_name( name, regs, info )
register char *name;
REGS *regs;
FILE_INFO *info;
{
/* If the name specifies an implicit file (ie. it asks for
* the directory rather than the files in the directory),
* modify it to ask for files (eg. ".." becomes "..\*.*").
* If the name is actually modified, a pointer to a modified
* copy of the original name is returned. Otherwise the
* original buffer is returned.
*/
static char buf[80] ; /* Place to put modified name */
register char *p = buf ; /* Always points into buf */
char *start_name = name; /* Remember start of name */
if( isrootdir(name) || (name[0] && name[1]==':' && !name[2]) )
{
/* Handle an explicitly requested root directory or
* the current directory on another disk.
*/
sprintf(buf, "%s*.*", name );
}
else if( !find_first( name, ALL, regs) )
{
/* Look for the indicated name & see if it's a directory.
* If so, append slash-*.* to the requested name
*/
if( !IS_SUBDIR(info) )
return name;
else
sprintf(buf, "%s/*.*", name );
}
else
{
/* If we get here then a non-existant file or directory
* was requested.
* If the name consists of nothing but the characters
* \ / . or a drive designator, assume that the root
* directory was requested and adjust the name
* accordingly.
*/
if( *name && name[1] == ':') /* Copy drive designator if */
{ /* one's present. */
*p++ = *name++ ;
*p++ = *name++ ;
}
if( has_only(name, ".\\/") )
strcpy( p, "/*.*" );
else
return( start_name );
}
return( buf );
}
/*----------------------------------------------------------------------*/
static int dirtoa( target, infop, graphics, pathname )
register char *target ;
char *pathname ;
register FILE_INFO *infop ;
unsigned graphics;
{
/* Convert directory entry held in infop to an ascii string
* in target. If Longfmt use a long format, if graphics then
* directory names are printed in bold face, else they're
* printed as "<name>." If pathname is true then the name
* will be preceeded with the full pathname.
*/
char *startstr = target;
int i;
if( Longfmt )
{
*target++ = ( IS_READONLY(infop) ) ? 'r' : '.' ;
*target++ = ( IS_HIDDEN (infop) ) ? 'h' : '.' ;
*target++ = ( IS_SYSTEM (infop) ) ? 's' : '.' ;
*target++ = ( IS_SUBDIR (infop) ) ? 'd' : '.' ;
*target++ = ( IS_DIRTY (infop) ) ? 'm' : '.' ;
sprintf(target, " %6ld %2d/%02d/%02d %2d:%02d:%02d - ",
infop->fi_fsize,
C_MONTH(infop), C_DAY(infop), C_YEAR(infop)-1900,
C_HR(infop), C_MIN(infop), C_SEC(infop) );
while( *target )
target++;
}
if( IS_SUBDIR(infop) && graphics )
target = cpy( target, BOLDFACE );
target = cpy ( target, pathname );
target = cptolower( target, infop->fi_name );
if( IS_SUBDIR(infop) && graphics )
target = cpy( target, ALL_OFF );
return( target - startstr );
}
/*----------------------------------------------------------------------*/
static int add_entry( infop, dp, path )
FILE_INFO *infop ;
register DIRECTORY *dp ;
char *path ;
{
/* Add an entry to the DIRECTORY structure. Return 0 if
* it was added, one if it wasn't.
*/
char buf[128] ;
register int len ;
/*
* If we're not printing hidden directories but the current
* directory is nonetheless hidden, return immediately.
* Similarly, return if the directory is full.
*/
if( !dp->hidden && (IS_HIDDEN(infop) || *infop->fi_name == '.') )
return 1;
if( dp->maxdirs <= 0 ) /* No more room in dirv. return */
return 0; /* error status */
/*
* Update the directory count or the file count as appropriate
* return immeadialy if we're looking at a file and we aren't
* supposed to list file. The same with directories.
*/
if( IS_SUBDIR(infop) )
{
if( dp->dirs )
dp->ndirs++ ;
else
return 1;
}
else
{
if( dp->files )
dp->nfiles++ ;
else
return 1;
}
/*
* Convert the FILE_INFO structure to an ascii string and put
* it into buf. Then malloc a chunk of memory the correct size,
* copy the ascii string there, and put the malloced memory
* into dirv at the correct place.
*/
Longfmt = dp->longf;
len = dirtoa( buf, infop, dp->graphics, path );
if( len > dp->width )
dp->width = len ;
if( *dp->lastdir = malloc(len + 1) )
{
strcpy( *dp->lastdir++, buf ) ;
/* Add file size to total. Note that the actual amount
* of space (# of clusters) used by the file on the
* disk is used.
*/
dp->nbytes += ROUND( infop->fi_fsize, Cluster_size );
--dp->maxdirs;
return 1;
}
fprintf(stderr,"Can't get memory for directory\n");
return 0;
}
/*----------------------------------------------------------------------*/
static void copy_path( dest, src )
char *dest, *src;
{
/* Copy only the pathname part of the file spec contained in
* src to dest. Path names longer than 64 characters are
* truncated so dest must be at least 64 characters long.
*/
register char *p, *slash;
for( p = slash = src ; *p ; p++ )
if( *p == '/' || *p == '\\' || *p == ':' )
slash = p + 1;
for(p = src; p < slash && p - src < 64 ; *dest++ = *p++ )
;
*dest = 0;
}
/*----------------------------------------------------------------------*/
static void clab( dest, src )
register char *dest, *src;
{
for(; *src ; src++, dest++ )
if( *src != '.')
*dest = *src ;
}
/*----------------------------------------------------------------------*/
static int cmp( pp1, pp2 )
char **pp1, **pp2;
{
/* Comparison routine needed for ssort(), it can deal with
* long format output. It also sorts names including
* numbers so that xxx10xx is put after xxx9xx
*/
register char *p1 = *pp1;
register char *p2 = *pp2;
int num1, num2;
if( Longfmt )
{
/* Skip forward to the '-' that will preceede
* the filename.
*/
while( *p1 && *p1 != '-' )
p1++;
while( *p2 && *p2 != '-' )
p2++;
}
/* The following code deals with getting similar names
* with numeric componants sorted correctly. If you don't
* want this, replace the following code with a
* return( strcmp(p1, p2) );
*
* The while statement says to loop if the characters are
* equal and non-null OR if both characters are digits.
* If the characters aren't digits, num1 is false and we'll
* just advance the pointers, otherwise we'll compare the
* numbers.
*/
while( (num1 = (isdigit(*p1) && isdigit(*p2))) || (*p1 == *p2 && *p1) )
{
if( !num1 )
{
p1++;
p2++;
}
else
{
/* *p1 and *p2 are both numbers. Extract the
* numeric values from the strings and
* compare those values, rather than the
* ASCII values. Advance p1 and p2 past
* the numbers.
*/
num1 = num2 = 0;
do {
num1 = (num1 * 10) + ( *p1++ - '0');
} while( isdigit(*p1) );
do {
num2 = (num2 * 10) + ( *p2++ - '0');
} while( isdigit(*p2) );
if( num1 != num2 )
return( num1 - num2 );
}
}
return( *p1 - *p2 );
}
/*----------------------------------------------------------------------*/
DIRECTORY *mk_dir( size )
register unsigned size;
{
/* Make a DIRECTORY with the indicated number of dirv entries.
* Note that since one dirv entry is declared as part of the
* DIRECTORY header, we'll actually have size+1 entries
* available, though the last one is never used. We allocate
* it so that we can terminate the list with a null
* entry, even if the list is full.
*/
register DIRECTORY *dp;
if( !( dp = (DIRECTORY *)calloc( (unsigned)1,
sizeof(DIRECTORY) + (size * sizeof(char *))) ))
return 0;
dp->maxdirs = size ;
dp->lastdir = dp->dirv;
return dp;
}
/*----------------------------------------------------------------------*/
del_dir( dp )
register DIRECTORY *dp;
{
/* Delete a directory made with a previous mk_dir call.
* Note that all the strings pointed to by dirv entries
* are assumed to have been gotten from malloc (this is
* always true if dir() is used to fill the strings.
*/
register char **v;
for( v = dp->dirv; v < dp->lastdir ; free( *v++ ) )
;
free( dp );
}
/*----------------------------------------------------------------------*/
dir( spec, dp )
char *spec;
DIRECTORY *dp;
{
/* Get a directory for the indicated spec. DOS wildcards are
* permitted. If the DIRECTORY pointed to by dp already has
* entries in it, new ones will be appended onto the existing
* ones. If *spec is null, no files will be gotten, this is
* useful if all you want is the volume label.
*
* Note that the DTA is not modified by this routine. It
* sets the DTA to its own address, but then restores the
* DTA before returning.
*/
REGS regs ; /* Needed for DOS calls */
FILE_INFO info ; /* DOS puts dirs here */
char path[80] ; /* place to put path */
char **firstdir; /* Used for sorting */
short seg,off ; /* Segment and offset of */
/* original DTA */
unsigned sec_per_cluster, /* Used to compute number of */
bytes_per_sector, /* bytes in a cluster */
garbage;
gregs( ®s ); /* Get the original DTA */
DOSCALL( GETDTA, regs );
seg = regs.x.es ; /* remember it in set:off */
off = regs.x.bx ;
regs.x.dx = (word) &info; /* Change the Disk Transfer Addr */
DOSCALL( SETDTA, regs ); /* to point at info structure */
/* Find the number of bytes/cluster on the indicated
* disk drive (or on the current drive if none is
* specified.
*/
if( !diskinfo( (!*spec||spec[1]!=':') ? 0 : (toupper(*spec)-'A')+1,
&sec_per_cluster, &bytes_per_sector, &garbage, &garbage))
fprintf(stderr,"dir: Can't access indicated disk\n");
Cluster_size = sec_per_cluster * bytes_per_sector ;
/* If a volume label is requested, get it and copy it into
* dp->vol_lab. Any imbedded '.'s are stripped by clab.
* If no volume label is present, the string is nulled.
*/
if( dp->label )
{
*dp->vol_label = 0;
if( spec[1] != ':' )
strcpy( path, "/*.*" );
else
{
*path = *spec ;
strcpy( path+1, ":/*.*" );
}
if( !find_first(path, LABEL, ®s) )
clab( dp->vol_label, info.fi_name );
}
/*
* Now get the directories:
*/
if( dp->exp && !haswild(spec) )
spec = fixup_name( spec, ®s, &info );
copy_path( path, dp->path ? spec : "" );
firstdir = dp->lastdir;
/* Now go look for the file:
*/
if( !find_first(spec, ATTRIBUTES, ®s) )
if( !add_entry(&info, dp, path) )
goto abort;
if( haswild(spec) )
while( !find_next( ®s ) )
if( !add_entry(&info, dp, path) )
goto abort;
if( dp->sort )
ssort( (char *)firstdir, dp->lastdir - firstdir,
sizeof(char*), cmp);
abort:
regs.x.ds = seg ; /* Restore the original disk */
regs.x.dx = off ; /* transfer address. */
DOSCALL( SETDTA, regs );
}