home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1996 September
/
Simtel-MSDOS-Sep1996-CD2.iso
/
disc2
/
c
/
update.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-25
|
19KB
|
597 lines
/* update - copy new/altered files onto another disk with confirmation
usage...
update [options] <source> <destination>
update [options] <destination>
update [options]
File names on the source and destination disks are
compared. If a file on the source disk is newer (more
recent creation date) than the corresponding file on
the destination disk, or if no file by that name exists
on the destination disk, the user is asked whether that
file should be copied. A .BAT file of the necessary
commands is written, and the requested copies are
performed by a copy of the command line interpreter
specified by the environment variable COMSPEC.
The default source is current drive. The default
destination is a compile time option (currently c:).
If no default destination is #defined, executing the
program with an empty command line will cause a usage
message to be printed.
The source and/or destination can be a pathname, and
the source can include a generic file name.
options...
-b make batch file $$.bat but don't execute it
-t today's files - copy only files less than 24 hours old
-q quiet - don't ask for confirmation
-r recursive - visit subdirectories recursively
-u update - don't copy new files
Options can appear anywhere on the command line, and
can be combined, as in:
update -tu i: c:
notes...
Requires getargs() (see file GETARGS.C in the IBM-PC
library), and exec() from the library supplied with
the DeSmet compiler. exec() takes two arguments, each
a pointer to a string. The first string is the
pathname of a program to be executed. The second
string is the rest of the command line that program
gets. For example,
exec("a:command.com","/cdir *.*")
uses a subsidiary copy of the command line interpreter
to print out a directory.
The source file was created with tabs set every four
columns.
author...
James R. Van Zandt (jrv@mitre-bedford)
revisions...
James R. Van Zandt VERSION 1.10, 15 Nov 86
Generic file names are allowed in source, and pathnames,
with or without trailing '\', are allowed in either source
or destination. Volume labels are never copied. Updates
can be to a specified subdirectory on the same disk.
James A. Mullens (jcm @ ornl-msr.arpa) VERSION 1.11, Jan 86
Bug and Syntax Cleaning
The new attribute byte specifies that system and hidden
files are always eligible for updating.
(To restrict to "normal" files only, pass 0.)
Empty command line results in usage message.
James R. Van Zandt VERSION 1.13, 6 Feb 87
Destination directory can be created if needed.
Warning printed if a file in the destination directory
is newer.
James R. Van Zandt VERSION 2.00, 7 Feb 87
Recursive option added.
*/
#define VERSION "2.00"
/*
If the following macro is defined, executing update with an empty
command line will copy new/updated files to the current directory
on the designated drive. If it is not defined, then executing
update with an empty command line will result in a usage message
(with mention of a default drive omitted).
*/
#define DEFAULT_DRIVE "c:"
#include <stdio.h>
/*---------------------------------------------------------------------------*/
/* typedefs and defines needed for getargs */
#define INTEGER 0
#define BOOLEAN 1
#define CHARACTER 2
#define STRING 3
#define PROC 4
typedef struct
{ unsigned arg ; /* command line switch */
unsigned type ; /* variable type (of those #defined above) */
int *variable ; /* pointer to variable */
char *errmsg ; /* pointer to error message */
} ARG;
typedef struct
{ char fi_resv[21]; /* bytes 0-20 reserved by DOS */
char fi_attrib; /* byte 21 File attribute */
long fi_time; /* bytes 22-23 Create/update time */
/* bytes 24-25 create/update date */
long fi_fsize; /* bytes 26-29 file size in bytes */
char fi_name[13]; /* bytes 30-42 file name & extension */
}
FILE_INFO;
#define SUBDIR 0x10
#define READONLY 0x01
#define HIDDEN 0x02
#define SYSTEM 0x04
#define VOLUMELABEL 0x08
#define ARCHIVED 0x20
#define IS_SUBDIR(p) ((p).fi_attrib & SUBDIR )
#define IS_READONLY(p) ((p).fi_attrib & READONLY )
#define IS_HIDDEN(p) ((p).fi_attrib & HIDDEN )
#define IS_SYSTEM(p) ((p).fi_attrib & SYSTEM )
#define IS_VOLUMELABEL(p) ((p).fi_attrib & VOLUMELABEL )
#define IS_ARCHIVED(p) ((p).fi_attrib & ARCHIVED )
extern unsigned _rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds,_doint();
extern char _carryf,_zerof;
/*--------------------------------------------------------------------*/
/* directory related BDOS function numbers */
#define FINDFIRST 0x4e
#define FINDNEXT 0x4f
#define SETDTA 0x1a
#define GETDTA 0x2f
#define MAXFILES 200 /* maximum number of files to check */
#define OPEN 0x3d /* dos function call to open a file */
#define CLOSE 0x3e /* dos function call to close a file */
#define DATETIME 0x57 /* " to get/set file's date & time */
#define BATFILE "$$.BAT" /* output file with copy commands */
#define DEFTIME 0 /* default time if file doesn't exist */
char source_file[40], source_pattern[40],source_drive[40]="";
char dest_file[40];
#ifdef DEFAULT_DRIVE
char dest_drive[40]=DEFAULT_DRIVE;
#else
char dest_drive[40];
#endif
char filepattern[40]="*.*";
char *(filev[MAXFILES]); /* pointers to names of source files */
long time[MAXFILES]; /* creation time/date for source files */
int filec=0; /* number of files to be checked on destination disk */
int today=0; /* nonzero if only today's files are to be checked */
int quiet=0; /* nonzero if user isn't to be asked to confirm */
int recursive=0; /* nonzero if subdirectories are to be updated too */
int updated_only=0; /* nonzero if user wants only updated files copied */
int batchfile_only=0; /* zero if user wants batchfile executed */
long cutoff; /* only files newer than this are to be checked */
int copying=0; /* number of files being copied */
FILE *out;
/*---------------------------------------------------------------------------
The time field is a 32 bit long consisting of the date
and time fields returned from a DOS 0x57 call. The date and time
are concataneted with the date in the most significant 16 bits and
the time in the least significant. This way they can be compared
as a single number.
*/
/*---------------------------------------------------------------------------*/
long gtime( file ) char *file;
{
/* Return the time and date for a file.
The DOS time and date are concatenated to form one large
number. Note that the high bit of this number will be set to 1
for all dates after 2043, which will cause the date comparisons
done in make() to fail. THIS ROUTINE IS NOT PORTABLE (because
it assumes a 32 bit long).
*/
short handle = 0 ; /* place to remember file handle */
long time ;
_rds=-1;
_rax=(OPEN<<8)|0; /* open the file */
_rdx=(short) file;
_doint(0x21);
if(_carryf) return DEFTIME; /* file doesn't exist */
handle=_rbx=_rax;
_rax=(DATETIME<<8)|0; /* get the time */
_doint(0x21);
if(_carryf) err("DOS returned error from date/time request");
time=(((long)(_rdx))<<16) | ((long)(_rcx)&0xffffL);
_rax=CLOSE<<8; /* close the file */
_doint(0x21);
if(_carryf) err("DOS returned error from file close request");
return time;
}
err(s) char *s;
{ fprintf(stderr,s);
exit(1);
}
find_first(filespec, attributes)
char *filespec; int attributes;
{ /* Get directory information for the indicated file.
Ambiguous file references are okay 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. */
_rds=-1;
_rdx=(short)filespec;
_rcx=attributes;
doscall(FINDFIRST);
return(_rax);
}
/*----------------------------------------------------------------------*/
find_next(t) char *t;
{ /* Get the next file in a 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. */
_rds=-1;
_rdx=t;
doscall(FINDNEXT);
return(_rax);
}
/*----------------------------------------------------------------------*/
doscall(id)
{ /* Do the DOS system call specified by "id" */
_rax=id<<8;
_doint(33);
}
copy(s,d) char *s,*d;
{ printf(" COPYING.");
fprintf(out,"copy %s %s\n",s,d);
copying++;
}
create(s) char *s;
{ printf(" CREATING.");
fprintf(out,"md %s\n", s);
copying++;
}
yes()
{ while(1)
{switch (getchar())
{case 'n':
case 'N': return 0;
case 'y':
case 'Y': return 1;
}
}
}
envsearch(target,value) char *target,*value;
{ char buf[256],*s,t[25],*env;
int nt;
s=t;
while(*target) *s++=toupper(*target++);
*s++= '='; *s=0;
nt = strlen(t);
_lmove(2,44,_showcs()-0x10,&env,_showds());
_lmove(256,0,env,buf,_showds());
s=buf;
while(*s)
{/* printf("examining entry: %s \n",s); */
if (strncmp(t,s,nt)) /* strings differ */
while(*s++) ;
else return (strcpy(value,s+nt));
}
*value=0; /* no value found */
}
help()
{ printf("update ver %s - move new/altered files to another disk \n",
VERSION);
puts("usage: update [options] <source> <destination> \n");
puts(" update [options] <destination> \n");
#ifdef DEFAULT_DRIVE
puts(" update [options] \n");
puts("Default source is current drive \n");
printf("Default destination is %s \n", DEFAULT_DRIVE);
#else
puts("Default source is current drive \n");
#endif
puts("Source and/or destination can be a pathname.\n");
puts("Source can include a generic file name.\n\n");
puts("Options are:\n");
puts(" -b make batch file $$.bat but don\'t execute it\n");
puts(" -t today's files - copy only files less than 24 hours old\n");
puts(" -q quiet - don't ask for confirmation\n");
puts(" -r recursive - repeat for subdirectories\n");
puts(" -u update - copy only altered files, not new ones\n");
puts("example: update -tu i: c:archives\\\n");
exit(0);
}
version() /* return MS-DOS version number. Version 2.01 returned as 201 */
{ extern unsigned _rax;
_rax=0x3000;
_doint(0x21);
return ( (_rax&0xff)<<8 | (_rax&0xff00)>>8 );
}
ARG Argtab[]={ {'b', BOOLEAN, &batchfile_only,"make $$.BAT but don\'t execute"}
{'t', BOOLEAN, &today, "today's files only"},
{'q', BOOLEAN, &quiet, "quiet"}
{'r', BOOLEAN, &recursive, "recursive"}
{'u', BOOLEAN, &updated_only, "copy updated files only"}
};
#define TABSIZE (sizeof(Argtab)/sizeof(ARG))
/*--------------------------------------------------------------------------*/
static FILE_INFO info ; /* DOS puts dirs here */
main( argc, argv )
int argc;
char **argv;
{ char *filename, command[40], ch, *r, *s, *t, *u;
int error, i;
if(
#ifndef DEFAULT_DRIVE
argc<=1 ||
#endif
(argc>1 && strcmp(argv[1],"?")==0)) help();
argc=getargs(argc,argv,Argtab,TABSIZE);
if(argc>2)
/* update <source drive> <destination drive> */
{strcpy(dest_drive,argv[2]);
s=t=argv[1];
while(*t) t++;
u=t-1; /* u points to last char in source path */
while(t>=s && *t!='\\' && *t!=':') t--;
if(t>=s) /* '\' or ':' was found... pathname was specified */
{r=source_drive;
while(s<=t) *r++=*s++;
*r=0;
}
if(t<u) /* characters follow the last '\' */
{if(index(t+1,'*') || index(t+1,'?'))
{
/*
printf("wild cards are included, so\n");
*/
goto gfn;
}
_rds=-1;
_rdx=(char *)&info; /* change the Disk Transfer Addr */
doscall(SETDTA); /* to point at info structure */
error=find_first(argv[1],0x16); /* find subdirectories
or other files but not volume labels */
if(error)
{printf("can\'t find %s - %s\n",argv[1],
(error==2)?"not a legal path name":
((error==18)?"no such file":
("unknown error code")
)
);
exit(1);
}
if(IS_SUBDIR(info))
{
/*
printf("%s is a subdirectory\n",argv[1]);
*/
strcpy(source_drive,argv[1]);
strncat(source_drive,"\\", 40);
}
else
{
gfn:
/*
printf("%s is not a subdirectory\n",argv[1]);
*/
strcpy(filepattern,t+1); /* file name was specified */
}
}
}
else if(argc>1)
/* "update <destination drive>" */
{strcpy(dest_drive,argv[1]);
}
/*
printf(" source pathname = \"%s\",",source_drive);
printf(" source filepattern = \"%s\"\n",filepattern);
*/
/*
printf(" destination pathname = \"%s\"\n",dest_drive);
*/
if(!(out=fopen(BATFILE,"w")))
err("can't open output file");
if(today)
cutoff=gtime(BATFILE)-0x10000; /* 24 hours ago */
else
cutoff=0;
_rds=-1;
_rdx=(char *)&info; /* change the Disk Transfer Addr */
doscall(SETDTA); /* to point at info structure */
update(source_drive, dest_drive);
fclose(out);
if(copying && !batchfile_only)
{if(version()<0x200)
{puts(
"DOS version 2.00 or higher required for automatic copying \n");
fallback();
}
envsearch("comspec",command);
if(find_first(command,0)) /* COMMAND.COM is missing */
{printf("%s is missing. \n",command);
fallback();
}
exec(command,strcat("/c\000 ",BATFILE));
}
if(!batchfile_only) unlink(BATFILE);
}
fallback()
{ puts("Please enter these commands... \n $$ \n erase $$.bat \n ");
exit(0);
}
update(source_drive, dest_drive) char *source_drive, *dest_drive;
{ int error, i, dest_existed, subc;
char ch, *s, **subv, *s_file, *s_drive, *d_file, *d_drive;
long td;
filec=0;
#ifdef DEBUG
printf("update(%s,%s)\n",source_drive,dest_drive);
#endif
strcpy(source_pattern, source_drive); /* start with source pathname */
strncat(source_pattern, filepattern, 40); /* add "*.*" or user's pattern */
error=find_first(source_pattern, 6);
while(!error)
{
if(strcmp(info.fi_name,BATFILE) /* we never copy our own .BAT file */
&& info.fi_time>cutoff )
{if(!(filev[filec]=malloc(strlen(info.fi_name)+1)))
err("can't allocate buffer space");
strcpy(filev[filec],info.fi_name);
time[filec]=info.fi_time;
filec++;
if(filec>=MAXFILES)
{fprintf(stderr,"too many files...checking only the first %d \n",
MAXFILES);
break;
}
}
error=find_next(source_pattern);
}
s=dest_drive+strlen(dest_drive)-1;
if(*s=='\\') *s=0;
dest_existed=is_directory(dest_drive);
if(dest_existed==2)
{printf("subdirectory name %s conflicts with existing file \
in destination directory\n"
,source_drive);
return;
}
if( !dest_existed )
{printf("destination directory %s doesn't exist", dest_drive);
if(quiet||(puts(" create it? "), yes()))
{create(dest_drive);
putchar('\n');
}
else
{putchar('\n');
return;
}
}
ch=dest_drive[strlen(dest_drive)-1];
if(ch!=':' && ch!='\\')
strncat(dest_drive,"\\", 40);
for(i=0; i<filec; i++)
{strcpy(source_file,source_drive);
strncat(source_file, filev[i], 40);
strcpy(dest_file,dest_drive);
strncat(dest_file, filev[i], 40);
if(dest_existed) td=gtime(dest_file);
else td=0;
if(td<time[i])
{if (td || !updated_only)
{printf("%-15s %12s",
filev[i],td?"updated":"didn't exist");
if(quiet||(puts(" copy it? "),yes()))
copy(source_file,dest_file);
putchar('\n');
}
}
else if(td>time[i])
{printf("%-15s WARNING: file in destination directory is newer!\n",
filev[i]);
}
free(filev[i]);
}
if(!recursive) return;
/*
record the names of all the subdirectories
*/
filec=0;
strcpy(source_pattern, source_drive); /* start with source pathname */
ch=source_pattern[strlen(source_pattern)-1];
if(ch!=':' && ch!='\\')
strncat(source_pattern,"\\", 40); /* add "\" if needed */
strncat(source_pattern, "*.*", 40); /* add "*.*" */
error=find_first(source_pattern, 0x10); /* look for subdirectories */
while(!error)
{if(IS_SUBDIR(info) && strcmp(info.fi_name,".") &&
strcmp(info.fi_name,".."))
{if(!(filev[filec]=malloc(strlen(info.fi_name)+1)))
err("can't allocate buffer space");
strcpy(filev[filec],info.fi_name);
filec++;
if(filec>=MAXFILES)
{fprintf(stderr,
"too many subdirectories...checking only the first %d \n",
MAXFILES);
break;
}
}
error=find_next(source_pattern);
}
if(!filec) return;
/*
make local copies of everything, lest it be overwritten
during the recursive call to update
*/
s_file=malloc(40);
s_drive=malloc(40);
d_file=malloc(40);
d_drive=malloc(40);
subv=malloc(filec*sizeof(filev[0]));
if(!subv || !s_file || !s_drive || !d_file || !d_drive)
err("can't allocate buffer space");
subc=filec;
strcpy(s_drive, source_drive);
strcpy(d_drive, dest_drive);
for (i=0; i<subc; i++) subv[i]=filev[i];
#ifdef DEBUG
printf("found subdirectories:");
for (i=0; i<subc; i++) printf("%s ", subv[i]);
putchar('\n');
#endif
for(i=0; i<subc; i++)
{strcpy(s_file, s_drive);
strncat(s_file, subv[i], 40);
ch=s_file[strlen(s_file)-1];
if(ch!=':' && ch!='\\')
strncat(s_file,"\\", 40);
strcpy(d_file, d_drive);
strncat(d_file, subv[i], 40);
update(s_file, d_file);
}
free(s_file); free(s_drive); free(d_file); free(d_drive); free(subv);
}
is_directory(d) char *d;
{ char *s;
int error;
if(d[1]==':') s=d+2; /* skip drive identifier if any */
else s=d;
if(*s=='\\') s++; /* skip leading '\' if any */
if(*s) /* something else in path...must be specifying a subdirectory */
{
/* printf("is_directory() checking destination <%s>\n", d); */
error=find_first(d, 0x10); /* look for subdirectories */
/* printf(" ...find_first() returns %d\n", error); */
if(error) return 0;
if (IS_SUBDIR(info)) return 1; /* it's a subdirectory */
return 2; /* it's a normal file */
}
return 1; /* current directory on destination drive always exists */
}