www.pudn.com > ZMODEM code.zip > ANYWHERE.C


/*************************************************************** 
*							       * 
*	   WHERE					       * 
*							       * 
* Where is a program to locate files on the PC hard disk.      * 
* It requires DOS 2.x or 3.x.				       * 
*							       * 
* The command line syntax is:				       * 
* where [starting directory]filename.ext		       * 
*							       * 
* Written by Mark S. Ackerman				       * 
* PROGRAM IS WRITTEN IN MARK WILLIAMS  M W C 8 6  LANGUAGE     * 
* Copyright 1984, 1985 by Mark S. Ackerman.  Permission is     * 
* granted for unlimited copies if not sold or otherwise        * 
* exchanged for gain.					       * 
*  PUBLISHED IN PC TECH JOURNAL - OCT '85 - VOL 3, NO. 10      * 
*							       * 
*  MODIFIED FOR LATTICE C VER 2.15 BY JOHN TEICHERT NOV 85     * 
*     Names shortened to 8 significant characters.	       * 
*     Elimination of PTOREG() function			       * 
*     flag86 defined to return flags from intdosx()	       * 
*     Use segread to set up regs for intdosx() function.       * 
*     Program modified to look for drive designator with colon.* 
*     DATE structure defined with bits high order to low order.* 
*     intrpt.h replaced with dos.h header file		       * 
*     rindex() function replaced with strrchr() function.      * 
*							       * 
*  MODIFIED FOR MICROSOFT V3 BY JOHN TEICHERT JAN 86.	       * 
*     flag86 REDEFINED to dos_result for  intdosx() ax reg     * 
*     modified to use flag in REGS structure.		       * 
*     DATE structure defined with bits low order to high order.* 
*     _stack will not produce large stack in ver 3.0 must use  * 
*	     link option or exemod program.		       * 
*							       * 
*   Added Code And Became ANYWHERE  JOHN TEICHERT FEB 86       * 
*							       * 
*	Taking advantange of V3 access to the environment      * 
*	string we set up the following. 		       * 
*							       * 
*	1. An Environment String indicating what disk drives   * 
*	   you want ANYWHERE to search as follows:	       * 
*							       * 
*	   AWDISKS=d:[;d:[;d:;...d:]]			       * 
*							       * 
*	      where d: is the drive specifier for one or more  * 
*	      fixed disk(s).				       * 
*							       * 
*	      drive specifiers are searched in the order they  * 
*	      are entered.				       * 
*							       * 
*	2. The user can specify the environment string with    * 
*	   the use of the set command in the autoexec.bat file.* 
*	   As an example:				       * 
*							       * 
*		set awdisks=c:;e:;d:			       * 
*							       * 
*	   would be placed into the autoexec.bat file.	       * 
*							       * 
*	With this modification the user has extended directory * 
*	capabilities by automatically searching all disk drives* 
*	listed in the environment string or isolated to a      * 
*	single drive by placing a drive specifier in the       * 
*	command line argument string.			       * 
*							       * 
*	Be sure to use the /stack option on the link with      * 
*	Microsoft V3 C compiler or stack problems will result  * 
*	if many subdirectories.  8K seems to work well.        * 
*							       * 
***************************************************************/ 
 
 
/*************************************************************** 
* The C header files					       * 
* These identify library routines like printf() and int86x()  * 
***************************************************************/ 
 
#include    /* standard i/o			      */ 
#include      /* functions for DOS interrupt calls     */ 
 
/*************************************************************** 
* Structure for MS-DOS date and time fields		       * 
* See pages 4-6 and 4-7 of the DOS 2.1 technical	       * 
* reference manual for more information 		       * 
* This structure is used in the next structure definition      * 
***************************************************************/ 
 
struct msdos_date 
	{ 
	unsigned ms_sec    : 5; /* time in 2 sec. int (5 bits)*/ 
	unsigned ms_min    : 6; /* minutes (6 bits)	      */ 
	unsigned ms_hour   : 5; /* hours (5 bits)	      */ 
	unsigned ms_day    : 5; /* day of month (5 bits)      */ 
	unsigned ms_month  : 4; /* month (4 bits)	      */ 
	unsigned ms_year   : 7; /* year since 1980 (7 bits)   */ 
	}; 
 
/*************************************************************** 
* Definition of DOS Disk Transfer Area (DTA)		       * 
***************************************************************/ 
 
/*************************************************************** 
* Structure filled in by MS-DOS for interrupt 21 calls	       * 
* See page 5-46 of the DOS 2.1 technical reference	       * 
* manual for more information				       * 
***************************************************************/ 
 
struct DTA 
	{ 
	char	DTA_dosinfo[21];       /* used by DOS		*/ 
	char	DTA_attr;	       /* file attribute byte	*/ 
	struct msdos_date DTA_date;    /* date struct. as above */ 
	long	DTA_size;	       /* file size		*/ 
	char	DTA_filename[13];      /* file name (w/o path)	*/ 
	}; 
 
 
/*************************************************************** 
*		Definitions of constants		       * 
***************************************************************/ 
 
#define carry_set 0x0001 /* mask for flag register	       */ 
			 /* for carry bit		       */ 
#define no_type   0x00	 /* no bits set on file attribute byte */ 
#define directory_type	 0x10 /* directory file bit on file    */ 
			      /* info word		       */ 
 
#define no_more_files	 18	/* DOS return code for		*/ 
				/* no more files		*/ 
 
#define end_of_string	 '\0'   /* C uses a binary zero to      */ 
				/* signal end of string 	*/ 
 
#define backslash '\\'   /* the backslash character           */ 
#define colon	  ':'    /* Drive separator  JT 11/85           */ 
#define semicolon ';'   /* Environment string drive separator   */ 
 
char *month[] = { 
		"Jan","Feb","Mar","Apr","May","Jun", 
		"Jul","Aug","Sep","Oct","Nov","Dec" 
		}; 
 
char *time_of_day[2] = {"AM","PM"}; 
 
 
/*************************************************************** 
*		Define the type "filename"                     * 
*			to be a string of 65 characters  -JT   * 
***************************************************************/ 
 
typedef char filename[65];		   /* Change to 65 -JT		     */ 
 
/*************************************************************** 
*							       * 
* The following filename strings are used in the program:      * 
*							       * 
*	chk_str 		filename to be searched for    * 
*				filename in the command line)  * 
*	dir_string		directory name to be searched  * 
*	new_dstr		directory name to be searched  * 
*				  on next recursive call       * 
*	cur_str 		temporary string for searching * 
*				  in a specific directory      * 
***************************************************************/ 
 
 
/*************************************************************** 
* Definition of any forward-referenced functions	       * 
***************************************************************/ 
 
char *DATE(); 
 
/*************************************************************** 
*		Global variables			       * 
***************************************************************/ 
 
filename chk_str;			/* this string "remembers" user input */ 
union	REGS	r8086;			/* structure to allow access to indiv.*/ 
					/*   registers for interrupts	      */ 
struct	SREGS	s8086;			/*   structure for segment registers  */ 
char		date_str[40];		/* print output string for dates      */ 
unsigned	dos_result;		/* Return code from DOS 	      */ 
 
/** 
*	FOLLOWING CODE COMMENTED OUT FOR V3.0 SINCE CAN'T FIND A WAY TO DO IT 
*	WITH A VARIABLE. 
**/ 
 
/*int		  _stack = 8192;	   Insure large stack to support */ 
/*					   recursion in look routine	 */ 
 
/*************************************************************** 
*		MAIN() -- the beginning of the code	       * 
***************************************************************/ 
 
main( argc, argv, envp ) 
int	argc; 
char	*argv[]; 
char	*envp[];			/* Version 3 pointer to environ */ 
{ 
/** 
*	External	function 
**/ 
char			*strrchr();	/* Lattice function which searches */ 
					/* for the last occurrance of the  */ 
					/* desired character		   */ 
 
char			*strchr();	/* Lattice function which searches */ 
					/* for the first occurrance of the */ 
					/* desired character		   */ 
 
filename	       dir_string;	/* directory to be searched	   */ 
 
char		       *usr_file;	/* address of filename in	   */ 
					/* command line argument	   */ 
					/* (ie, the filename)		   */ 
 
char *last_loc; 			/* address of last backslash in    */ 
					/* command line argument	   */ 
 
char *dos_parm; 			/* address of			   */ 
					/* command line argument	   */ 
 
int  lst_dchr;				/* last character		   */ 
					/* in directory string		   */ 
 
/** added for ANYWHERE JT **/ 
int			strcmpi();	/* Microsoft V3 function to do a     */ 
					/*   string compare without regard   */ 
					/*   to case.			     */ 
void			strcat();	/* String concatenate function	     */ 
#define     MAXEDRV	16		/* Set max number of drives	     */ 
char			*strdup();	/* MSC string dup function	     */ 
char			*getcwd();	/* MSC ver3 function to return	     */ 
					/* current working directory	     */ 
char			*envdup;	/* pointer to duplicate of env str   */ 
char			*envdrv[MAXEDRV];   /* pointer to each drive	     */ 
					/* in envdup			     */ 
filename		env_dir_string; /* Environment directory string      */ 
char			**cur_envp;	/* current environmnet array pointer */ 
char			*env_stp;	/* Current environment string pointer*/ 
char			*env_chp;	/* environment character pointer     */ 
static	char		envid[]="AWDISK=";      /* Our environment string id */ 
char			*envid_p;	/* current id string ptr	     */ 
filename		dos_cwd;	/* buffer to hold cur working dir    */ 
int			env_cnt;	/* count of number of drives	     */ 
int			envd_len;	/* length of the environment string  */ 
int			wi;		/* A working integer		     */ 
struct	{ 
	unsigned	drive	: 1;	/* drive specifier found in com line */ 
	unsigned	found	: 1;	/* environment contains AWDISK=      */ 
	unsigned	srchcwd : 1;	/* Search for current work dir	     */ 
	unsigned	addcwd	: 1;	/* Add current working directory     */ 
	} flag; 
 
/******************************************************** 
*	check number of incoming arguments		* 
*	if incorrect, write an error message		* 
********************************************************/ 
 
if (argc != 2) 
    { 
    printf( "ERR  usage is:   AW    [starting directory]filename.ext\n\n"); 
    printf( "Environment string is: AWDISK=d: [;d: [;....d:] ]\n"); 
    return; 
    } 
 
/**	Added for Lattice C Version 2.15 JT 
*	Initialize the segment structure for later dos interrupt calls. 
**/ 
 
segread( &s8086 );		/* initialize segments for interrupt calls */ 
dos_result = 0; 		/* init interrupt return variable MSCV3.   */ 
 
/** 
*	dos_parm is set to the first argument in the 
*	command line 
**/ 
 
dos_parm = *(++argv); 
 
/** 
*	If a drive specifier is found we do not want to go through overhead 
*	of environment string search.		AW - JT 
*	If no drive specifier is found we will extract the drive of the 
*	    current working directory to be added to the array of environment 
*	    drives. 
**/ 
flag.srchcwd = 1;			/* Init true until command line      */ 
					/*     option added.		     */ 
if ( strchr( dos_parm, colon ) ) 
    { 
    flag.drive = 1;			/* Set drive specifier true	     */ 
    flag.found = 0;			/* Set environment found false	     */ 
    } 
else 
    { 
    /** 
    *	Drive flag is set to zero 
    *	If the search current working directory flag is true then: 
    *	    Current working directory is obtained and the drive isolated from 
    *		from the directory string. 
    **/ 
    flag.drive = 0; 
    if ( flag.srchcwd ) 
	{ 
	getcwd(dos_cwd,sizeof(dos_cwd)-1); 
	env_chp = strchr( dos_cwd, colon ); 
	*++env_chp = end_of_string; 
	} 
    } 
/** 
*   We check to see if there was a drive specifier found.  If so we 
+	want to continue rather than check the environment string for the 
*	AWDISK= parameter 
**/ 
if (flag.drive) 
    { 
    /** 
    *	There was a drive specifier so we zero the env_cnt variable 
    **/ 
    env_cnt = 0; 
    } 
else 
    { 
    /** 
    *	The drive specifier was  found in the DOS command argument therefore 
    *	    the environment strings will be searched for the AWDISK= parameter 
    **/ 
    cur_envp = envp;		    /* init current environ pointer */ 
    flag.found = 0;		    /* init flag found false	    */ 
    /** 
    *	    Check each environment string to see if it is the AWDISK 
    *	    environment string.  The envp array is terminated with 
    *	    a NULL pointer.  The search is terminated when found is 
    *	    true or the environment pointer is NULL. 
    **/ 
    while( *cur_envp && !flag.found ) 
	{ 
	envid_p = envid;	/* init pointer to our environ id */ 
	env_chp = *cur_envp;	/* init pointer to environ string */ 
	flag.found = 1; 	/* Set flag found = true to enter loop */ 
	/** 
	*   Compare each character in the string to AWDISK= 
	**/ 
	for ( env_cnt=0; (env_cnt<(sizeof(envid)-1)) && flag.found; env_cnt++) 
	    { 
	    if ( *envid_p++ != *env_chp++ ) 
		flag.found = 0; 
	    else 
		flag.found = 1; 
	    } 
 
	/** 
	*   If found true then copy string to our buffer 
	*   Replace ';' with 0x00 
	*   Count the number of drives found 
	*   Set addcwd true to add the current working directory 
	**/ 
	if ( flag.found ) 
	    { 
	    flag.addcwd = 1; 
	    /** 
	    *	Create a duplicate of the environment string 
	    *	If it cannot be created we will probably fail but allow 
	    *	continuation just in case 
	    **/ 
	    if (!(envdup = strdup(env_chp))) 
		{ 
		fprintf( stderr, "Memory alloc problems\n" ); 
		env_cnt = 0;		/* zero count cause we're goin out   */ 
		break;			/* allow current drive to continue   */ 
		} 
	    /** 
	    *	Examine the environment string for a ";" 
	    *	Replace each semicolon with a "\0" to terminate the string 
	    *	Increment the env_cnt variable for each drive in the system 
	    *	    that the user wished to examine 
	    *	Place the pointer to the beginning of drive in the envdrv 
	    *	    variable. 
	    **/ 
	    envd_len = strlen(envdup);	/* get length of environment string  */ 
	    env_cnt  = 0;		/* Set count of drives to zero	     */ 
	    env_stp  = envdup;		/* Set the string pointer to current */ 
	    while ( (envd_len > 0) && (env_cnt < MAXEDRV) ) 
		{ 
		/** 
		*   Look for the semicolon 
		**/ 
		if (env_chp = strchr(env_stp,semicolon)) 
		    { 
		    /** 
		    *	We have a semicolon : See if its false 
		    **/ 
		    if (env_chp-env_stp) 
			{ 
			/** 
			*   There is a string so put in array 
			*   Decrement the length field 
			*   Change the value of the semi colon to a end of str 
			*   Set up a new pointer beyond env_stp 
			**/ 
			envdrv[env_cnt++] = env_stp; 
			envd_len = envd_len-((env_chp-env_stp)+1); 
			*env_chp = end_of_string; 
			/** 
			*   Check the environment entry against the current 
			*	working directory.  If equal set addcwd to 
			*	false 
			**/ 
			if ( strcmpi( dos_cwd, env_stp ) == 0 ) 
			    flag.addcwd = 0; 
			env_stp = ++env_chp; 
			} 
		    else 
			{ 
			/** 
			*   There was no string just a semicolon 
			*   Decrement the length field 
			*   setup a new pointer beyond the semi-colon 
			**/ 
			--envd_len; 
			env_stp = ++env_chp; 
			} 
		    } 
		else 
		    { 
		    /** 
		    *	There was no semi-colon so we are on the last drive 
		    *	    entry. 
		    *	Place address in array and increment count 
		    *	Set the remaining string length to zero to exit. 
		    *	See if last entry matches current working directory 
		    *	    if so don't add it. 
		    **/ 
		    envdrv[env_cnt++] = env_stp; 
		    envd_len = 0; 
		    if ( strcmpi( dos_cwd, env_stp ) == 0 ) 
			flag.addcwd = 0; 
		    if ( flag.addcwd ) 
			{ 
			if ( (env_cnt < MAXEDRV) && (flag.srchcwd) ) 
			    envdrv[env_cnt++] = dos_cwd; 
			} 
		    } 
		} 
	    } 
	else 
	    { 
	    /** 
	    *	This environment string is not the one we want therefore 
	    *	Increment current environment pointer and continue 
	    **/ 
	    cur_envp++; 
	    } 
	}   /* End While */ 
    }	/* End Else */ 
 
/** 
* The dos_parm is then searched for the last 
* occurrence of a backslash to find the end of 
* the directory name. 
**/ 
 
last_loc = strrchr(dos_parm,backslash); 
 
 
/******************************************************** 
* If there was not a backslash (and therefore the	* 
*     beginning directory is the root directory)	* 
* begin 						* 
*   copy command line argument into chk_str		* 
*   copy root directory into dir_string 		* 
* end							* 
* else							* 
* (if there was a backslash and therefore a beginning	* 
*     directory specified in the command line)		* 
* begin 						* 
*   set the usr_file to the next character		* 
*	past the backslash				* 
*   copy the usr_file into chk_str			* 
*   copy the command line argument into 		* 
*	dir_string					* 
*   terminate dir_string just after the 		* 
*	last backslash (therefore leaving only the	* 
*	the directory name in the string)		* 
* end							* 
********************************************************/ 
 
if (last_loc == NULL) 
    { 
    /** 
    * Since no backslash we'll still check for a drive designator LC 2.14 -JT 
    **/ 
    last_loc = strchr(dos_parm, colon); 
    if (last_loc == NULL) 
	{ 
	strcpy(chk_str,dos_parm); 
	strcpy(dir_string,"\\"); 
	} 
    else 
	{ 
	usr_file = last_loc + 1; 
	strcpy(chk_str,usr_file); 
	strcpy(dir_string,dos_parm); 
	lst_dchr = usr_file - dos_parm; 
	dir_string[lst_dchr++] = backslash;	/* Fake path */ 
	dir_string[lst_dchr]   = end_of_string; /* terminate directory str */ 
	} 
    } 
else 
    { 
    usr_file = last_loc + 1; 
    strcpy(chk_str,usr_file); 
    strcpy(dir_string,dos_parm); 
    lst_dchr = usr_file - dos_parm; 
    dir_string[lst_dchr] = end_of_string; 
    } 
 
 
/** 
*   if the environment string was found concatenate environment drives 
*	with the directory string that was extracted. 
*   else look for just the directory string 
**/ 
 
if ( flag.found) 
    { 
    for ( wi = 0; wi < env_cnt; wi++ ) 
	{ 
	/** 
	*   Search each directory in the environment array 
	**/ 
	strcpy( env_dir_string, envdrv[ wi ] ); 
	strcat( env_dir_string, dir_string ); 
	LOOK( env_dir_string ); 
	} 
    } 
else 
    { 
    /** 
    *	There is no environment loop so look only for directory string 
    **/ 
    LOOK(dir_string); 
    } 
 
return; 
 
} 
 
/**/ 
/********************************************************* 
*	LOOK is the recursive procedure in WHERE	 * 
*	It is called once for each subdirectory 	 * 
*********************************************************/ 
 
LOOK(dir_string) 
char *dir_string; 
{ 
 
struct DTA cur_DTA;		/* used to return data from DOS  */ 
 
filename new_dstr;		/* the directory to be		 */ 
				/* searched on the next 	 */ 
				/* call to LOOK()		 */ 
 
filename cur_str;		/* temporary filename		 */ 
				/* string for searching for	 */ 
				/* directories			 */ 
 
 
/** 
* Form cur_str by copying dir_string and		* 
*    and then concatenating "*.*" to look through all   * 
*    files						* 
**/ 
 
strcpy(cur_str,dir_string); 
strcat(cur_str,"*.*"); 
 
/** 
* Set the Disk Transfer Area in DOS to the cur_DTA	* 
*    structure						* 
* Get the first subdirectory in this directory		* 
**/ 
 
SET_DTA(&cur_DTA); 
GET_FIRST(cur_str,directory_type); 
 
/** 
* while there are more subdirectories in this directory * 
* begin 						* 
*   double check for proper directories (see text)	* 
*   if a directory					* 
*   begin						* 
*     set up the new_dstr for the			* 
*     next call to LOOK (see text)			* 
*     call LOOK 					* 
*     reset Disk Transfer Address (see text)		* 
*   end 						* 
*   look for next directory				* 
* end							* 
**/ 
 
while (!(r8086.x.cflag & carry_set)) 
    { 
    if (cur_DTA.DTA_attr == directory_type && 
       cur_DTA.DTA_filename[0] != '.') 
	{ 
	strcpy(new_dstr,dir_string); 
	strcat(new_dstr,cur_DTA.DTA_filename); 
	strcat(new_dstr,"\\"); 
	LOOK(new_dstr); 
	SET_DTA(&cur_DTA); 
	} 
    GET_NEXT(); 
    } 
 
/******************************************************** 
* if there are no more subdirectories in this directory * 
*   look for files					* 
* else							* 
*   print an error message				* 
********************************************************/ 
 
if (r8086.x.ax == no_more_files) 
    GET_FILES(dir_string,&cur_DTA); 
else 
    printf("problem with looking thru %s\n",dir_string); 
return; 
 
} 
 
 
/******************************************************** 
* GET_FILES						* 
* is called once per directory to look for the		* 
*   actual files matching the search string		* 
********************************************************/ 
 
GET_FILES(dir_string,cur_DTA) 
char *dir_string; 
struct DTA *cur_DTA; 
{ 
 
filename cur_str; 
 
/******************************************************** 
* Form cur_str by copying dir_string into  * 
*   it and then concatenating the chk_str onto	   * 
*   the end						* 
********************************************************/ 
 
strcpy(cur_str,dir_string); 
strcat(cur_str,chk_str); 
 
/********************************************************* 
* Get the first file that matches cur_str	 * 
********************************************************/ 
 
GET_FIRST(cur_str,no_type); 
 
/******************************************************** 
* while there are more files that match the search	* 
*   string:						* 
* begin 						* 
*   print the file information				* 
*   get the next file					* 
* end							* 
********************************************************/ 
 
while (!(r8086.x.cflag & carry_set)) 
    { 
    printf(" %10ld %s  %s%s\n", (*cur_DTA).DTA_size, 
	   DATE(&((*cur_DTA).DTA_date)), dir_string, 
	   (*cur_DTA).DTA_filename); 
    GET_NEXT(); 
    } 
 
/********************************************************* 
* if error in looking for a file			* 
*    print error message and return			* 
********************************************************/ 
 
if (r8086.x.ax != no_more_files) 
    printf("problem with looking for %s\n",cur_str); 
 
return; 
 
} 
 
/******************************************************** 
* GET_NEXT does an interrupt 21h, function 4Fh		* 
* Function 4fh is Get next directory entry		* 
********************************************************/ 
 
GET_NEXT() 
 
{ 
r8086.x.ax = 0x4f00; 
 
dos_result = int86x( 0x21, &r8086, &r8086, &s8086 ); 
 
return; 
} 
 
 
/******************************************************** 
* SET_DTA does an interrupt 21h, function 1Ah		* 
*   The DS:DX pair is set to the address of the 	* 
*   cur_DTA data structure			    * 
********************************************************/ 
 
SET_DTA(cur_DTA) 
struct DTA	*cur_DTA; 
{ 
 
r8086.x.ax = 0x1a00; 
r8086.x.dx = (int)cur_DTA;		/* set offset to disk transfer area */ 
 
dos_result = int86x(0x21, &r8086, &r8086, &s8086 ); 
 
return; 
 
} 
 
/******************************************************** 
* GET_FIRST does an interrupt 21h, function 4Eh 	* 
*   The CX register is set to either normal or		* 
*   directory type (see text)				* 
*   The DS:DX pair is set to the address of the 	* 
*   search string					* 
********************************************************/ 
 
GET_FIRST(sea_str,filetype) 
char *sea_str; 
int filetype; 
{ 
 
r8086.x.ax = 0x4e00;				/* Set DOS function	 */ 
r8086.x.cx = filetype;				/* Set search attributes */ 
r8086.x.dx = (int)    sea_str;		  /* Set address of string */ 
 
dos_result = int86x( 0x21, &r8086, &r8086, &s8086 ); 
 
return; 
 
} 
 
/******************************************************** 
* DATE takes the date field from the current DTA	* 
*   structure and returns a string containing the	* 
*   information in formatted ASCII			* 
********************************************************/ 
 
char *DATE(dateptr) 
struct msdos_date *dateptr; 
{ 
 
sprintf(date_str, "%02d-%02d-%02d %02d:%02d %s", 
	dateptr->ms_month, dateptr->ms_day, 
	dateptr->ms_year+80, (dateptr->ms_hour)%12, 
	dateptr->ms_min, time_of_day[((dateptr->ms_hour)/12)]); 
return(date_str); 
 
}