/***************************************************************************** * ADS - Archive Date Set * * A program to set the date/time stamp of members of ARC files * * Ross Presser - 12/14/88 * *****************************************************************************/ /* Usage: ADS [u/d:] mm/dd/yy [hh:mm[:ss][p]] [amember] where = ARC file name, required (user/drive: optional) mm/dd/yy = date stamp, required [hh:mm[:ss][p]] = time stamp, optional. 24 hour clock, or append 'p' for pm. defaults to midnight [amember] = member filespec, possibly wild. Defaults to *.* */ #include /* structure of an ARC header */ struct { char arcmark; /* always 1Ah */ char arcver; /* compression version */ char name[13]; /* filename fn.ext, null-terminated */ char crsize[4]; /* compressed file size, poor man's LONG */ unsigned _date; /* MS-DOS style date stamp */ unsigned _time; /* MS-DOS style time stamp */ unsigned crc; /* file CRC */ char ucsize[4]; /* original file size, poor man's LONG */ } *archdr; /* various variables */ char buf[256]; /* two-sector buffer */ int u, /* ARC file descriptor */ changed; /* count of changed members */ char pos[4]; /* offset from start of file in bytes */ char *matchmem; /* member filespec to match */ unsigned date, /* new date stamp */ time; /* new time stamp */ int notfirst; /* false only before first header is read */ main(argc,argv) char *argv[]; { int open(); /* low level file open */ /* must be at least 2 arguments */ if(argc<3) usage(""),exit(); /* Open the ARC file. Try .ARK and .ARC extensions if none given */ u=tryext2(open,argv[1],2,".ARK",".ARC"); /* Process date stamp */ if (EOF==index(argv[2],"/")) /* date must be mm/dd/yy */ usage("Invalid date\n"),exit(); date=getdate(argv[2]); /* 3rd arg might be timestamp */ if(argc>=4 && EOF!=index(argv[3],":")) { /* assume yes if : in it */ time=gettime(argv[3]); /* process the time */ argv[3]=argv[4]; --argc; /* delete arg from list */ } else time=0; /* if no time, use midnight */ /* 4th arg is member filespec, if there */ matchmem="???????????"; /* default *.* first */ if(argc>=4) hack(matchmem,argv[3]); /* change fn.ext to filenameext */ itol(pos,0); /* init file position to 0 */ /* main loop */ while(1) { /* read next header */ gethdr(); /* must begin with legal arc mark */ if(archdr->arcmark != 0x1a) { puts("Invalid archive format\n"); break; } /* version 0 means end of file */ if(archdr->arcver == 0) break; /* if filename matches our filespec, change it */ if(match(archdr->name,matchmem)) { printf("Changing %s\n",archdr->name); /* say it */ archdr->_date=date; /* change date */ archdr->_time=time; /* and time */ ++changed; /* count it */ } } /* Finish up */ printf("%d members changed.\n",changed); /* say how many */ close(u); } /********************************************************************* This function moves the file position to the beginning of the next arc header, and calculates where the one after that will be in prepar- ation for the next call. On all calls except the first, it writes the buffer back into the file to preserve any changes that may have been made. */ gethdr() { unsigned rec, /* record number */ offs; /* offset within record */ char temp[4]; /* scratch LONG */ /* If not first call, write the buffer */ if(notfirst) { seek(u,-2,1); /* back up 2 sectors */ write(u,buf,2); /* write the buffer */ } /* Convert long pos to rec # and offset. In plain language, rec = pos/128; offs = pos % 128 */ offs=ltoi(lmod(temp,pos,itol(temp,128))); rec=ltoi(ldiv(temp,pos,itol(temp,128))); /* Go to the record computed and read 2 sectors */ seek(u,rec,0); read(u,buf,2); /* Set arc header within buffer */ archdr=buf+offs; /* Compute position of next header */ ladd(pos,pos,itol(temp,29)); /* bump past this header */ ladd(pos,pos,archdr->crsize); /* bump past this file */ notfirst=1; /* flag not first */ } /********************************************************** This function checks whether the unparsed filename s matches the parsed filename t. */ match (s,t) char *s,*t; { char a[12], /* scratch string */ *b; /* scratch string ptr */ hack(a,s); /* parse the filename */ b=a; while(*b) /* match until EOS */ if(*b!=*t && *t!='?') /* either a match or a ? is ok */ return(0); /* otherwise fail */ else ++b,++t; /* Bump ptrs */ return(1); /* succeed */ } /************************************************************** This function parses a filename from fn.ext to fn------ext. */ hack(k,j) char *j,*k; { int i; /* scratch index */ i=0; while(*j && *j != '.') /* do first 8 char */ if (*j=='*') { /* expand * to ?s */ while(i<8) k[i++]='?'; while(*j && *j!= '.') ++j; } else k[i++]=*j++; /* otherwise copy */ while(i<8) k[i++]=' '; /* pad out name to 8 */ if(*j) ++j; /* move past possible dot */ while(*j) /* do last 3 char */ if (*j=='*') { /* expand * to ?s */ while(i<11) k[i++]='?'; while(*j) ++j; } else k[i++]=*j++; /* otherwise copy */ while(i<11) k[i++]=' '; /* pad out ext to 3 */ k[11]='\0'; /* finish string with null */ return(k); } /************************************************************************ This function takes an mm/dd/yy string and returns an MS-DOS date stamp. */ getdate(s) char *s; { int mm,dd,yy; mm=dd=yy=0; /* init to 0 */ sscanf(s,"%d/%d/%d",&mm,&dd,&yy); /* read from string */ if(yy<100) yy += 1900; /* force century */ if(yy<1980) yy += 100; /* disallow less than 1980 */ if(mm<1 || mm>12 || dd<1 || dd>31 || yy>2108) /* range checking */ usage("Invalid date!\n"),exit(); return (((yy-1980)<<9) + (mm<<5) + dd); /* convert to MS-DOS fmt */ } gettime(s) char *s; { int hh,mm,ss; hh=mm=ss=0; /* init to 0 */ sscanf(s,"%d:%d:%d",&hh,&mm,&ss); /* read from string */ if(s[strlen(s)-1]=='P') hh+=12; /* suffix p for P.M. */ /* range checking */ if(hh<0 || hh>23 || mm<0 || mm>59 || ss<0 || ss>59) { printf("\7%s is an invalid time, using 12:00a\n",s); return(0); /* invalid times default to midnight */ } return((hh<<11)+(mm<<5)+ss/2); } /* This function for debugging purposes; prints out archive header */ /* puthdr(ahp) struct _archdr *ahp; { char s[15]; printf("%02x %d %12s %-10s %04x %04x %04x %-10s\n", ahp->arcmark,ahp->arcver,ahp->name, ltoa(s,ahp->crsize),ahp->_date,ahp->_time, ahp->crc,ltoa(s,ahp->ucsize)); } */ /* This will try to open fn alone, or with ext1 or ext2 appended */ tryext2(fx,fn,mod,ext1,ext2) char *fn,*ext1,*ext2; int (*fx)(),mod; { int z; char s[15]; if((z=(*fx)(fn,mod))!=ERROR) return(z); if(EOF==index(fn,".")) { strcpy(s,fn); strcat(s,ext1); if((z=(*fx)(s,mod))!=ERROR) return(z); strcpy(s,fn); strcat(s,ext1); if((z=(*fx)(s,mod))!=ERROR) return(z); } printf("\7%s not found!\n",fn); exit(); } /* This prints the optional error message, plus a usage message */ usage(s) char *s; { puts(s); puts("\nUsage:\n"); puts("\tADS [u/d:] mm/dd/yy [hh:mm[:ss][p]] [amember]\n"); puts("where\n"); puts("\t\t= ARC file name, required. user/drive: optional\n"); puts("\tmm/dd/yy\t= date stamp, required. century optional\n"); puts("\t[hh:mm[:ss][p]]\t= time stamp, optional. 24 hour clock, or append\n"); puts("\t\t\t 'p' for pm. defaults to midnight\n"); puts("\t[amember]\t= member filespec, possibly wild. Defaults to *.*\n"); }