/*
   miniunz.c
   Version 1.01e, February 12th, 2005

   Portions copyright (c) 2007-2008 Symbian Software Ltd. All rights reserved.
   Copyright (C) 1998-2005 Gilles Vollant
   
*/

#include "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unzip.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#define CASESENSITIVITY (0)
#define WRITEBUFFERSIZE (8192)
#define MAXFILENAME (256)


 
char* get_value(char* var, char** env_list);


/*
  mini unzip, demo of unzip package

  usage :
  Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]

  list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
    if it exists
*/

#if 0 // not supported currently
/* change_file_date : change the date/time of a file
    filename : the filename of the file where date/time must be modified
    dosdate : the new date at the MSDos format (4 bytes)
    tmu_date : the SAME new date at the tm_unz format */
void change_file_date(/*const*/ char *filename, uLong dosdate, tm_unz tmu_date)
{
#ifdef unix
  struct utimbuf ut;
  struct tm newdate;
  if (dosdate == 0) { /* do nothing. to avoid warnings */ };
  newdate.tm_sec = tmu_date.tm_sec;
  newdate.tm_min=tmu_date.tm_min;
  newdate.tm_hour=tmu_date.tm_hour;
  newdate.tm_mday=tmu_date.tm_mday;
  newdate.tm_mon=tmu_date.tm_mon;
  if (tmu_date.tm_year > 1900)
      newdate.tm_year=tmu_date.tm_year - 1900;
  else
      newdate.tm_year=tmu_date.tm_year ;
  newdate.tm_isdst=-1;

  ut.actime=ut.modtime=mktime(&newdate);
  utime(filename,&ut);
#endif
}

#endif

/* mymkdir and change_file_date are not 100 % portable
   As I don't know well Unix, I wait feedback for the unix portion */

int mymkdir(/*const*/ char* dirname) 
{
    int ret=0;
#ifdef unix
    ret = mkdir(dirname, 0775);
#endif
    return ret;
}

int makedir (char *newdir)
{
  char *buffer ;
  char *p;
  int  len = (int)strlen(newdir);

  if (len <= 0)
    return 0;

  buffer = (char*)malloc(len+1);
  strcpy(buffer,newdir);

  if (buffer[len-1] == '/') {
    buffer[len-1] = '\0';
  }
  if (mymkdir(buffer) == 0)
    {
      free(buffer);
      return 1;
    }

  p = buffer+1;
  while (1)
    {
      char hold;

      while(*p && *p != '\\' && *p != '/')
        p++;
      hold = *p;
      *p = 0;
      if (mymkdir(buffer) == -1)
        {
          printf("Couldn't create directory %s\n",buffer);
          free(buffer);
          return -1;
        }
      if (hold == 0)
        break;
      *p++ = hold;
    }
  free(buffer);
  return 1;
}



void do_banner()
{
    printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
    printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
}

void do_help()
{
    printf("Usage : unzip [-e] [-x] [-l] [-o] file.zip [file_to_extr.] [-d extractdir]\n\n" \
           "  -e  Extract without pathname (junk paths)\n" \
           "  -x  Extract with pathname\n" \
           "  -l  list files\n" \
           "  -d  directory to extract into\n" \
           "  -o  overwrite files without prompting\n\n");    
}

int do_list(unzFile uf)
{
    uLong i;
    unz_global_info gi;
    int err;
    unz_file_info file_info;
    uLong ratio=0;
    //const 
    char *filename_inzip = (char*)ALLOC(256); //heap_alloc
    
    err = unzGetGlobalInfo (uf,&gi);
    if (err!=UNZ_OK)
        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
   	printf(" Length  Size   Ratio  Date    Time   Name\n");
   	printf(" ------  ------ -----  ----    ----   ----\n");
    for (i=0;i<gi.number_entry;i++)
    {
        ratio = 0;
        err = unzlocal_GetCurrentFileInfoInternal(uf,&file_info, NULL,
        			filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
        if (err != UNZ_OK)
        {
            printf("error %d with zipfile in unzlocal_GetCurrentFileInfoInternal\n", err);
            TRYFREE(filename_inzip);
            return err;
        }
        if (file_info.uncompressed_size>0) {
            ratio = (file_info.compressed_size*100)/file_info.uncompressed_size;
        }
        printf("%7lu  %7lu %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %s\n",        
                file_info.uncompressed_size,
                file_info.compressed_size,
                ratio,
                (uLong)file_info.tmu_date.tm_mon + 1,
                (uLong)file_info.tmu_date.tm_mday,
                (uLong)file_info.tmu_date.tm_year % 100,
                (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
                filename_inzip);
        if ((i+1)<gi.number_entry)
        {
            err = unzGoToNextFile(uf);
            if (err!=UNZ_OK)
            {
                printf("error %d with zipfile in unzGoToNextFile\n",err);
                break;
            }
        }
    }
    TRYFREE(filename_inzip);
    return 0;
}


int do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, 
							    int* popt_overwrite, const char* password)
{
	int err = UNZ_OK;
    char* filename_withoutpath = NULL;
    char* p = NULL;
    FILE *fout=NULL;
    void* buf = NULL;
    uInt size_buf;
    unz_file_info file_info;
    //const 
	char* write_filename;
    int skip=0;
    char rep=0;
    FILE* ftestexist;
    char answer[10]; //CHANGED to 10 from 128
    int ret;
    char *filename_inzip = (char*)ALLOC(256);     //char filename_inzip[256];  changed to heap_alloc    
    char c;
    
    err = unzlocal_GetCurrentFileInfoInternal(uf, &file_info, NULL,
    		filename_inzip, 
			256, NULL, 0, NULL, 0);  
    if (err != UNZ_OK)
        {
    	printf("Error %d with zipfile in unzGetCurrentFileInfo\n", err);
        return err;
        }
    size_buf = WRITEBUFFERSIZE;
    buf = (void*)malloc(size_buf);   
    if (buf == NULL)
    {
        printf("Error allocating memory\n");
        return UNZ_INTERNALERROR;
    }

    p = filename_withoutpath = filename_inzip;
    while ((*p) != '\0')
    {
        if (((*p)=='/') || ((*p)=='\\'))
            filename_withoutpath = p+1;
        p++;
    }
    if ((*filename_withoutpath)=='\0')
    { 
        if ((*popt_extract_without_path)==0)
        {
            printf("Creating directory: %s\n", filename_inzip);
            mymkdir(filename_inzip);
        }
    }
    else
    {
        if ((*popt_extract_without_path)==0)
            write_filename = filename_inzip;
        else
            write_filename = filename_withoutpath;
        err = unzOpenCurrentFilePassword(uf,password);
        if (err!=UNZ_OK)
        {
            printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
        }

        if (((*popt_overwrite)==0) && (err==UNZ_OK))
        {
            ftestexist = fopen(write_filename,"rb");            
            if (ftestexist!=NULL)
            {
                fclose(ftestexist);
                do
                {
                    printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
                    fflush(NULL);
                    ret = scanf("%1s",answer);
                    if (ret != 1) {
                    #ifdef __SYMBIAN32__
                        if (buf) 
                    		free(buf);
					#endif//__SYMBIAN32__                        
                    	return -1;
                    }
                    rep = answer[0] ;
                    if ((rep>='a') && (rep<='z'))
                        rep -= 0x20;
                }
                while ((rep!='Y') && (rep!='N') && (rep!='A'));
            }

            if (rep == 'N')
                skip = 1;

            if (rep == 'A')
                *popt_overwrite=1;
        }
        if ((skip==0) && (err==UNZ_OK))
        {
            fout=fopen(write_filename,"wb");

            /* some zipfile don't contain directory alone before file */
            if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
                                (filename_withoutpath!=(char*)filename_inzip))
            {
                c=*(filename_withoutpath-1);
                *(filename_withoutpath-1)='\0';
                makedir(write_filename);
                *(filename_withoutpath-1)=c;
                fout=fopen(write_filename,"wb");
            }
            if (fout==NULL)
            {
                printf("Error opening %s\n",write_filename);
            }
        }
        if (fout!=NULL)
        {
            printf("Extracting: %s\n",write_filename);

            do
            {
                err = unzReadCurrentFile(uf,buf,size_buf);
                if (err<0)
                {
                    printf("Error %d with zipfile in unzReadCurrentFile\n",err);
                    break;
                }
                if (err>0)
                    if (fwrite(buf,err,1,fout)!=1)
                    {
                        printf("Error in writing extracted file\n");
                        err=UNZ_ERRNO;
                        break;
                    }
            }
            while (err>0);
            if (fout)
                    fclose(fout);
#if 0 // not supported currently
            if (err==0)
                change_file_date(write_filename,file_info.dosDate,
                                 file_info.tmu_date);
#endif            
        }
        if (err==UNZ_OK)
        {
            err = unzCloseCurrentFile (uf);
            if (err!=UNZ_OK)
            {
                printf("Error %d with zipfile in unzCloseCurrentFile\n",err);
            }
        }
        else
            unzCloseCurrentFile(uf); /* don't lose the error */
    }

    if (buf) {
    	free(buf);
    }
    if (filename_inzip) {
    	free(filename_inzip);
    }     
    return err;
}


int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password)
{
    uLong i;
    unz_global_info gi;
    int err;

    err = unzGetGlobalInfo (uf,&gi);
    if (err != UNZ_OK) {
        printf("Error %d with zipfile in unzGetGlobalInfo \n", err);
        return -1;
    }
    for (i=0;i<gi.number_entry;i++)
    {
        if (do_extract_currentfile(uf,&opt_extract_without_path,
                                      &opt_overwrite,
                                      password) != UNZ_OK)
            break;
        if ((i+1)<gi.number_entry)
        {
            err = unzGoToNextFile(uf);
            if (err!=UNZ_OK)
            {
                printf("Error %d with zipfile in unzGoToNextFile\n",err);
                break;
            }
        }
    }

    return 0;
}

int do_extract_onefile(unzFile uf, const char* filename, int opt_extract_without_path,
					    int opt_overwrite, const char* password)
{
    if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
    {
        printf("file %s not found in the zipfile\n",filename);
        return 2;
    }

    if (do_extract_currentfile(uf,&opt_extract_without_path,
                                      &opt_overwrite,
                                      password) == UNZ_OK)
        return 0;
    else
        return 1;
}



int main(int argc, char *argv[], char** env_list)
{
    //const 
    char *zipfilename=NULL;
    //const 
    char *filename_to_extract=NULL;
    //const 
    char *password=NULL;
    char *filename_try = (char*)ALLOC(MAXFILENAME+16);  // heap_alloc
    int i;
    int opt_do_list=0;
    int opt_do_extract=1;
    int opt_do_extract_withoutpath=0;
    int opt_overwrite=0;
    int opt_extractdir=0;
    //const 
    char *dirname=NULL;
    unzFile uf=NULL;
    char *p = NULL;
  	int err1 = 0;
  	
    if (argc==1)
    {
        do_help();
        free(filename_try);
        return 0;
    }
    else
    {
#ifdef __SYMBIAN32__  // <hack> changing directory to the working dir
	char* path = get_value("PWD", env_list); 
	if(path) {
		if (chdir(path) == -1) {
				printf("%s: %s\n", path, strerror(errno));
		        free(filename_try);
				return -1;
		}
	}
#endif //__SYMBIAN32__ 	// </hack>
        for (i=1;i<argc;i++)
        {
            if ((*argv[i])=='-')
            {
            	p = argv[i]+1;

                while ((*p)!='\0')
                {
                    char c=*(p++);;
                    if ((c=='l') || (c=='L'))
                        opt_do_list = 1;
                    if ((c=='v') || (c=='V'))
                        opt_do_list = 1;
                    if ((c=='x') || (c=='X'))
                        opt_do_extract = 1;
                    if ((c=='e') || (c=='E'))
                        opt_do_extract = opt_do_extract_withoutpath = 1;
                    if ((c=='o') || (c=='O'))
                        opt_overwrite=1;
                    if ((c=='d') || (c=='D'))
                    {
                        opt_extractdir=1;
                        dirname=argv[i+1];
                        i++;
                    }

                    if (((c=='p') || (c=='P')) && (i+1<argc))
                    {
                        password=argv[i+1];
                        i++;
                    }
                }
            }
            else
            {
                if (zipfilename == NULL)
                    zipfilename = argv[i];
                else if ((filename_to_extract==NULL) && (!opt_extractdir))
                        filename_to_extract = argv[i] ;
            }
        }
    }

    if (zipfilename!=NULL)
    {
        strncpy(filename_try, zipfilename,MAXFILENAME-1);
        /* strncpy doesnt append the trailing NULL, of the string is too long. */
        filename_try[ MAXFILENAME ] = '\0';
        uf = unzOpen(zipfilename);
    }
    if (uf==NULL)
    {
        printf("Cannot open %s\n", zipfilename );
        free(filename_try);
        return -1;
    }

		    if (opt_do_list==1) 
    {
    	free(filename_try);
        return do_list(uf); 
    }
    else if (opt_do_extract==1)
    {
        if (opt_extractdir && chdir(dirname)) 
        {
          printf("Error changing into %s, aborting\n", dirname);
          free(filename_try);
          return -1;
        }
      
        if (filename_to_extract == NULL)
        {
            err1 = do_extract(uf,opt_do_extract_withoutpath,opt_overwrite,password);
        }
        else
        {
        	err1 = do_extract_onefile(uf,filename_to_extract,
        				opt_do_extract_withoutpath,opt_overwrite,password);
        }
		free(filename_try);
		return err1;
    }
    unzCloseCurrentFile(uf);    
	free(filename_try);
    return 0;
}
