home *** CD-ROM | disk | FTP | other *** search
-
- /* 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 */
- }
-