#include    <stdio.h>
#include    <stdlib.h>
#include    <io.h>
#include    <fcntl.h>
#include    <dos.h>
#include    "zip.h"

/****************************************************************************
 *									    *
 *  zipot - change time stamp of ZIP file to be same as its latest contents *
 *									    *
 *  Initial release : Feb 19, 1989  Russ Herman 			    *
 *  Revision 1.1    : Feb 25, 1989					    *
 *		      Error in time calculation 			    *
 *									    *
 ****************************************************************************/

#define     BUFLEN     512
char	    buf[BUFLEN];		/* place to read in file	     */
long	    buf0_off;			/* file offset corresponding to buf  */
unsigned    buflen;			/* size of the buffer		     */
unsigned    bufgood;			/* amount of valid info in buffer    */

#ifdef DEBUG
#define REG
#else
#define REG register
#endif

/*  prototypes								     */
long	    find_cdt(int);
int	    otzip(char *);
void	    main(int, char **);


/* rummage backwards thru the file looking for the trailer signature	     */
long find_cdt(int zip_handle)
{
    REG char *p;
    REG int i;
    int front = 0;

    bufgood = buflen;
    while (buf0_off >= 0 && !front)
    {
	front = (buf0_off == 0);
	if (buf0_off < BUFLEN)
	{   /* short block						     */
	    bufgood = (unsigned) buf0_off;  /* words to read		     */
	    buf0_off = 0L;		    /* position in file 	     */
	}

	/* position to start of block					     */
	if (lseek(zip_handle, buf0_off, 0) < 0L)
	{
	    perror("cannot position ZIPfile");
	    return(-2l);
	}

	/* read some							     */
	if (read(zip_handle, buf, bufgood) < 0)
	{
	    perror("error reading ZIPfile");
	    return(-3L);
	}

	/* step backwards thru what we just read			    */
	for(i=bufgood-4,p=&buf[bufgood-4]; i>=0; i--,p--)
	{
	    if (*(unsigned long *)p == CDT_SIG)
		return(buf0_off + i);	/* exit on signature match	    */
	}

	/* We reread the first 4 bytes of each block as a quick&dirty way of *
	 * handling the case where the signature splits our buffer block     */
	buf0_off = buf0_off - bufgood + 4;
   }
   return(-1L); 			  /* Couldn't find signature         */
}


/* Process a single ZIPfile						     */
int otzip(char *fname)
{
    int zip_handle;
    int i, ret, later;
    unsigned early_date, early_time;
    long next_cdh;
    long cdt_f_off;
    long zip_fsize;
    CDH *cdhp, cdh;
    CDT *cdtp, cdt;
#ifdef __TURBOC__
    union REGS tregs;
#endif

    early_date = 0;
    early_time = 0;
    /* open the file							     */
    if ((zip_handle=open(fname, O_RDONLY+O_BINARY)) < 1)
    {
	perror(fname);
	return(97);
    }

    /* go to EOF							     */
    if ((zip_fsize=lseek(zip_handle, 0l, 2)) < 0L)
    {
	perror("cannot get to ZIPfile EOF");
	close(zip_handle);
	return(98);
    }
    if (zip_fsize < (long) BUFLEN)
	buf0_off = 0;
    else
	buf0_off = zip_fsize - buflen;

    /* look for an "end of central directory record" (cdt)                   */
    if ((cdt_f_off=find_cdt(zip_handle)) < 0)
    {
	if (cdt_f_off == -1L)
	{
	    fprintf(stderr, "%s: not a ZIPfile\n", fname);
	    close(zip_handle);
	    return(97);
	}
	else
	{
	    fprintf(stderr, "find_cdt code %d finding ZIPfile central directory\n",
		    (int) -cdt_f_off);
	    close(zip_handle);
	    return(99);
	}
    }

    /* reread the cdt to make sure we get it all			     */
    i = (lseek(zip_handle, cdt_f_off, 0) >= 0L);
    if (i)
	i = (read(zip_handle, (char *)&cdt, sizeof(cdt)-2) > 0);
    if (!i)
    {
	perror("error rereading ZIPfile cdt");
	close(zip_handle);
	return(96);
    }

#ifdef DEBUG
    printf("%lx sig %08lx dno %u dno_cdh %u cnt_here %u cnt_all %u\n",
	    cdt_f_off, cdt.sig, cdt.dno, cdt.dno_cdh,
	    cdt.cnt_here, cdt.cnt_all);
    printf("\tcd_size %lu cd_off %lu cmnt_len %u\n",
	    cdt.cd_size, cdt.cd_off, cdt.cmnt_len);
#endif

    if (cdt.dno != 0 || cdt.dno_cdh != 0)
    {
	fprintf(stderr, "multivolume ZIPOT not yet implemented\n");
	close(zip_handle);
	return(95);
    }

    /* point to the first file header in the central directory structure     */
    ret = 0;
    next_cdh = cdt.cd_off;
    cdh.sig = 0L;

    /* process each file header in the central directory structure	     */
    for (i=cdt.cnt_all; i>0; i--)
    {
	/* position to and read a file header				     */
	if (lseek(zip_handle, next_cdh, 0) < 0L)
	{
	    perror("cannot position to ZIPfile header");
	    ret = 94;
	    break;
	}
	if (read(zip_handle, (char *)&cdh, sizeof(cdh)-2) <= 0)
	{
	    perror("cannot read ZIPfile header");
	    ret = 93;
	    break;
	}
#ifdef DEBUG
	printf("sig %lx comp_ver %u extr_ver %u bit_flag %04x method %u\n",
		cdh.sig, cdh.comp_ver, cdh.extr_ver, cdh.bit_flag, cdh.method);
	printf("\tftime %04x fdate %04x crc %08lx comp_size %lu ur_size %lu\n",
		cdh.ftime, cdh.fdate, cdh.crc, cdh.comp_size, cdh.ur_size);
	printf("\tfname_len %u extra_len %u cmnt_len %u dno %u\n",
		cdh.fname_len, cdh.extra_len, cdh.cmnt_len, cdh.dno);
	printf("\tint_attr %04x ext_attr %08lx lfh_off %08lx\n",
		cdh.int_attr, cdh.ext_attr, cdh.lfh_off);
#endif
	/* is there really a header there ?				     */
	if (cdh.sig != CDH_SIG)
	{
	    fputs("ZIPfile header expected but not found\n", stderr);
	    ret = 92;
	    break;
	}

	/* stash latest file timestamp of the ZIP			     */
	if (!(later = (cdh.fdate>early_date)))
	    later = (cdh.fdate==early_date && cdh.ftime>early_time);
	if (later)
	{
	    early_date = cdh.fdate;
	    early_time = cdh.ftime;
	}

	/* compute offset of next header				     */
	next_cdh += sizeof(cdh) - 2 + cdh.fname_len + cdh.extra_len +
		    cdh.cmnt_len;
	cdh.sig = 0L;
    }
#ifdef DEBUG
    printf("early_date %04x, early_time %04x\n", early_date, early_time);
#endif

#ifdef	__TURBOC__
    tregs.h.ah = 0x57;
    tregs.h.al = 0x01;
    tregs.x.bx = zip_handle;
    tregs.x.cx = early_time;
    tregs.x.dx = early_date;
    intdos(&tregs, &tregs);
#else
    _dos_setftime(zip_handle, early_date, early_time);
#endif
    close(zip_handle);
    return(ret);
}


void main(int argc, char **argv)
{
    int i, ret;

    for (i=1; i<argc; i++)
    {
	buflen = BUFLEN;
	ret = otzip(argv[i]);
	if (ret != 0 && ret != 97)
	    fprintf(stderr, "    argument %s return code %d\n", argv[i], ret);
    }
    exit(0);
}
