which included commits to RCS files with non-trunk default branches. git-svn-id: svn://10.65.10.50/trunk@5403 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1911 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1911 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*---------------------------------------------------------------------------
 | |
| 
 | |
|   win32.c
 | |
| 
 | |
|   32-bit Windows-specific (NT/95) routines for use with Info-ZIP's UnZip 5.3
 | |
|   and later.
 | |
| 
 | |
|   Contains:  GetLoadPath()
 | |
|              Opendir()
 | |
|              Readdir()
 | |
|              Closedir()
 | |
|              process_defer_NT()   process any deferred items
 | |
|              SetSD()              set security descriptor on file
 | |
|              EvalExtraFields()    evaluate and process and extra field NOW
 | |
|              IsWinNT()            indicate type of WIN32 platform
 | |
|              test_NT()            test integrity of NT security data
 | |
|              utime2FileTime()
 | |
|              NTQueryTargetFS()
 | |
|              UTCtime2Localtime()
 | |
|              NTtzbugWorkaround()
 | |
|              getNTfiletime()
 | |
|              close_outfile()
 | |
|              isfloppy()
 | |
|              IsVolumeOldFAT()
 | |
|              IsFileNameValid()
 | |
|              do_wild()
 | |
|              mapattr()
 | |
|              mapname()
 | |
|              map2fat()
 | |
|              checkdir()
 | |
|              version()
 | |
|              stat_bandaid()       [Watcom only]
 | |
|              getch()              [Watcom only]
 | |
| 
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
| 
 | |
| #define UNZIP_INTERNAL
 | |
| #include "unzip.h"
 | |
| #include <windows.h>   /* must be AFTER unzip.h to avoid struct G problems */
 | |
| #ifdef __RSXNT__
 | |
| #  include "win32/rsxntwin.h"
 | |
| #endif
 | |
| #include "win32/nt.h"
 | |
| 
 | |
| 
 | |
| #if (defined(__GO32__) || defined(__EMX__))
 | |
| #  include <dirent.h>        /* use readdir() */
 | |
| #  define MKDIR(path,mode)   mkdir(path,mode)
 | |
| #  define Opendir  opendir
 | |
| #  define Readdir  readdir
 | |
| #  define Closedir closedir
 | |
| #  define zdirent  dirent
 | |
| #  define zDIR     DIR
 | |
| #else /* !(__GO32__ || __EMX__) */
 | |
| #  define MKDIR(path,mode)   mkdir(path)
 | |
| 
 | |
|    typedef struct zdirent {
 | |
|        char    reserved [21];
 | |
|        char    ff_attrib;
 | |
|        short   ff_ftime;
 | |
|        short   ff_fdate;
 | |
|        long    size;
 | |
|        char    d_name[MAX_PATH];
 | |
|        int     d_first;
 | |
|        HANDLE  d_hFindFile;
 | |
|    } zDIR;
 | |
| 
 | |
|    static zDIR           *Opendir  (const char *n);
 | |
|    static struct zdirent *Readdir  (zDIR *d);
 | |
|    static void            Closedir (zDIR *d);
 | |
| #endif /* ?(__GO32__ || __EMX__) */
 | |
| 
 | |
| 
 | |
| /* Function prototypes */
 | |
| #ifdef NTSD_EAS
 | |
|    static int  SetSD(__GPRO__ char *path, PVOLUMECAPS VolumeCaps,
 | |
|                      uch *eb_ptr, unsigned eb_len);
 | |
|    static int  EvalExtraFields(__GPRO__ char *path, uch *ef_ptr,
 | |
|                                unsigned ef_len);
 | |
| #endif
 | |
| 
 | |
| #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
 | |
|    static void utime2FileTime(time_t ut, FILETIME *pft);
 | |
|    static int NTQueryTargetFS(char *path);
 | |
| #endif
 | |
| #ifdef NT_TZBUG_WORKAROUND
 | |
|    static time_t UTCtime2Localtime(time_t utctime);
 | |
|    static void NTtzbugWorkaround(time_t ut, FILETIME *pft);
 | |
| #endif /* NT_TZBUG_WORKAROUND */
 | |
| 
 | |
| static int  getNTfiletime   (__GPRO__ FILETIME *pModFT, FILETIME *pAccFT,
 | |
|                              FILETIME *pCreFT);
 | |
| static int  isfloppy        (int nDrive);
 | |
| static int  IsVolumeOldFAT  (char *name);
 | |
| static int  IsFileNameValid (char *name);
 | |
| static void map2fat         (char *pathcomp, char **pEndFAT);
 | |
| 
 | |
| 
 | |
| /* static int created_dir;      */     /* used by mapname(), checkdir() */
 | |
| /* static int renamed_fullpath; */     /* ditto */
 | |
| /* static int fnlen;            */     /* ditto */
 | |
| /* static unsigned nLabelDrive; */     /* ditto */
 | |
| 
 | |
| extern char Far TruncNTSD[];    /* in extract.c */
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifdef SFX
 | |
| 
 | |
| /**************************/
 | |
| /* Function GetLoadPath() */
 | |
| /**************************/
 | |
| 
 | |
| char *GetLoadPath(__GPRO)
 | |
| {
 | |
| #ifdef MSC
 | |
|     extern char *_pgmptr;
 | |
|     return _pgmptr;
 | |
| 
 | |
| #else    /* use generic API call */
 | |
| 
 | |
|     GetModuleFileName(NULL, G.filename, FILNAMSIZ-1);
 | |
|     _ISO_INTERN(G.filename);    /* translate to codepage of C rtl's stdio */
 | |
|     return G.filename;
 | |
| #endif
 | |
| 
 | |
| } /* end function GetLoadPath() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #else /* !SFX */
 | |
| 
 | |
| #if (!defined(__GO32__) && !defined(__EMX__))
 | |
| 
 | |
| /**********************/        /* Borrowed from ZIP 2.0 sources            */
 | |
| /* Function Opendir() */        /* Difference: no special handling for      */
 | |
| /**********************/        /*             hidden or system files.      */
 | |
| 
 | |
| static zDIR *Opendir(n)
 | |
|     const char *n;          /* directory to open */
 | |
| {
 | |
|     zDIR *d;                /* malloc'd return value */
 | |
|     char *p;                /* malloc'd temporary string */
 | |
|     WIN32_FIND_DATA fd;
 | |
|     int len = strlen(n);
 | |
| 
 | |
|     /* Start searching for files in the MSDOS directory n */
 | |
| 
 | |
|     if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
 | |
|         (p = malloc(strlen(n) + 5)) == NULL)
 | |
|     {
 | |
|         if (d != (zDIR *)NULL)
 | |
|             free((void *)d);
 | |
|         return (zDIR *)NULL;
 | |
|     }
 | |
|     INTERN_TO_ISO(n, p);
 | |
|     if (p[len-1] == ':')
 | |
|         p[len++] = '.';   /* x: => x:. */
 | |
|     else if (p[len-1] == '/' || p[len-1] == '\\')
 | |
|         --len;            /* foo/ => foo */
 | |
|     strcpy(p+len, "/*");
 | |
| 
 | |
|     if (INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFile(p, &fd))) {
 | |
|         free((zvoid *)d);
 | |
|         free((zvoid *)p);
 | |
|         return NULL;
 | |
|     }
 | |
|     strcpy(d->d_name, fd.cFileName);
 | |
| 
 | |
|     free((zvoid *)p);
 | |
|     d->d_first = 1;
 | |
|     return d;
 | |
| 
 | |
| } /* end of function Opendir() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************/        /* Borrowed from ZIP 2.0 sources            */
 | |
| /* Function Readdir() */        /* Difference: no special handling for      */
 | |
| /**********************/        /*             hidden or system files.      */
 | |
| 
 | |
| static struct zdirent *Readdir(d)
 | |
|     zDIR *d;                    /* directory stream from which to read */
 | |
| {
 | |
|     /* Return pointer to first or next directory entry, or NULL if end. */
 | |
| 
 | |
|     if ( d->d_first )
 | |
|         d->d_first = 0;
 | |
|     else
 | |
|     {
 | |
|         WIN32_FIND_DATA fd;
 | |
| 
 | |
|         if ( !FindNextFile(d->d_hFindFile, &fd) )
 | |
|             return NULL;
 | |
| 
 | |
|         ISO_TO_INTERN(fd.cFileName, d->d_name);
 | |
|     }
 | |
|     return (struct zdirent *)d;
 | |
| 
 | |
| } /* end of function Readdir() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /***********************/
 | |
| /* Function Closedir() */       /* Borrowed from ZIP 2.0 sources */
 | |
| /***********************/
 | |
| 
 | |
| static void Closedir(d)
 | |
|     zDIR *d;                    /* directory stream to close */
 | |
| {
 | |
|     FindClose(d->d_hFindFile);
 | |
|     free(d);
 | |
| }
 | |
| 
 | |
| #endif /* !__GO32__ && !__EMX__ */
 | |
| #endif /* ?SFX */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifdef NTSD_EAS
 | |
| 
 | |
| /*********************************/
 | |
| /*  Function process_defer_NT()  */
 | |
| /*********************************/
 | |
| 
 | |
| void process_defer_NT(__G)
 | |
|     __GDEF
 | |
| {
 | |
|     /* process deferred items */
 | |
| 
 | |
|     unsigned long dir, bytes;
 | |
|     unsigned long dirfail, bytesfail;
 | |
| 
 | |
| #ifndef NO_NTSD_WITH_RSXNT
 | |
|     ProcessDefer(&dir, &bytes, &dirfail, &bytesfail);
 | |
| #else
 | |
|     dir = bytes = dirfail = bytesfail = 0L;
 | |
| #endif
 | |
| 
 | |
|     if (!G.tflag && (G.qflag < 2)) {
 | |
|         if (dir)
 | |
|             Info(slide, 0, ((char *)slide,
 | |
|               "    updated: %lu directory entries with %lu bytes security",
 | |
|               dir, bytes));
 | |
|         if (dirfail)
 | |
|             Info(slide, 0, ((char *)slide,
 | |
|               "     failed: %lu directory entries with %lu bytes security",
 | |
|               dirfail, bytesfail));
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************/
 | |
| /*  Function SetSD()  */   /* return almost-PK errors */
 | |
| /**********************/
 | |
| 
 | |
| static int SetSD(__G__ path, VolumeCaps, eb_ptr, eb_len)
 | |
|     __GDEF
 | |
|     char *path;
 | |
|     PVOLUMECAPS VolumeCaps;
 | |
|     uch *eb_ptr;
 | |
|     unsigned eb_len;
 | |
| {
 | |
|     ulg ntsd_ucSize = makelong(eb_ptr + (EB_HEADSIZE+EB_NTSD_LSIZE));
 | |
|     uch ntsd_Version = *(eb_ptr + (EB_HEADSIZE+EB_NTSD_VERSION));
 | |
|     uch *security_data;
 | |
|     int error;
 | |
| 
 | |
|     if (eb_ptr == NULL || eb_len < EB_NTSD_L_LEN)
 | |
|         return PK_OK;  /* not a valid NTSD extra field:  assume OK */
 | |
| 
 | |
|     /* check if we know how to handle this version */
 | |
|     if (ntsd_Version > EB_NTSD_MAX_VER_SUPPORT)
 | |
|         return PK_OK;
 | |
| 
 | |
|     if (ntsd_ucSize > 0L && eb_len <= (EB_NTSD_L_LEN + 6))
 | |
|         return IZ_EF_TRUNC;               /* no compressed data! */
 | |
| 
 | |
|     /* allocate storage for uncompressed data */
 | |
|     security_data = (uch *)malloc((extent)ntsd_ucSize);
 | |
|     if (security_data == NULL)
 | |
|         return PK_MEM4;
 | |
| 
 | |
|     error = memextract(__G__ security_data, ntsd_ucSize,
 | |
|       (eb_ptr + (EB_HEADSIZE+EB_NTSD_L_LEN)), (ulg)(eb_len - EB_NTSD_L_LEN));
 | |
| 
 | |
|     if (error == PK_OK) {
 | |
| #ifdef NO_NTSD_WITH_RSXNT
 | |
|         Info(slide, 0, ((char *)slide,
 | |
|           "%s port does not yet support setting security", "RSXNT"));
 | |
|         /* error = PK_OK; */  /* GRR:  change to PK_WARN? */
 | |
| #else /* !NO_NTSD_WITH_RSXNT */
 | |
|         if (SecuritySet(path, VolumeCaps, security_data)) {
 | |
|             error = PK_COOL;
 | |
|             if (!G.tflag && (G.qflag < 2) &&
 | |
|                 (!(VolumeCaps->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
 | |
|                 Info(slide, 0, ((char *)slide, " (%ld bytes security)",
 | |
|                   ntsd_ucSize));
 | |
|         }
 | |
| #endif /* ?NO_NTSD_WITH_RSXNT */
 | |
|     }
 | |
| 
 | |
|     free(security_data);
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /********************************/   /* scan extra fields for something */
 | |
| /*  Function EvalExtraFields()  */   /*  we happen to know */
 | |
| /********************************/
 | |
| 
 | |
| static int EvalExtraFields(__G__ path, ef_ptr, ef_len)
 | |
|     __GDEF
 | |
|     char *path;
 | |
|     uch *ef_ptr;
 | |
|     unsigned ef_len;
 | |
| {
 | |
|     int rc = PK_OK;
 | |
| 
 | |
|     if (!G.X_flag)
 | |
|         return PK_OK;  /* user said don't process ACLs; for now, no other
 | |
|                           extra block types are handled here */
 | |
| 
 | |
|     while (ef_len >= EB_HEADSIZE)
 | |
|     {
 | |
|         unsigned eb_id = makeword(EB_ID + ef_ptr);
 | |
|         unsigned eb_len = makeword(EB_LEN + ef_ptr);
 | |
| 
 | |
|         if (eb_len > (ef_len - EB_HEADSIZE)) {
 | |
|             /* discovered some extra field inconsistency! */
 | |
|             Trace((stderr,
 | |
|               "EvalExtraFields: block length %u > rest ef_size %u\n", eb_len,
 | |
|               ef_len - EB_HEADSIZE));
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         switch (eb_id)
 | |
|         {
 | |
|             /* process security descriptor extra data if:
 | |
|                  Caller is WinNT AND
 | |
|                  Target local/remote drive supports acls AND
 | |
|                  Target file is not a directory (else we defer processing
 | |
|                    until later)
 | |
|              */
 | |
|             case EF_NTSD:
 | |
|                 if (IsWinNT()) {
 | |
|                     VOLUMECAPS VolumeCaps;
 | |
| 
 | |
|                     /* provide useful input */
 | |
|                     VolumeCaps.dwFileAttributes = G.pInfo->file_attr;
 | |
|                     VolumeCaps.bUsePrivileges = (G.X_flag > 1);
 | |
| 
 | |
| #ifndef NO_NTSD_WITH_RSXNT
 | |
|                     /* check target volume capabilities - just fall through
 | |
|                      * and try if fail */
 | |
|                     if (GetVolumeCaps(G.rootpath, path, &VolumeCaps) &&
 | |
|                         !(VolumeCaps.dwFileSystemFlags & FS_PERSISTENT_ACLS))
 | |
|                     {
 | |
|                         rc = PK_OK;
 | |
|                         break;
 | |
|                     }
 | |
| #endif
 | |
|                     rc = SetSD(__G__ path, &VolumeCaps, ef_ptr, eb_len);
 | |
|                 } else
 | |
|                     rc = PK_OK;
 | |
|                 break;
 | |
| 
 | |
| #if 0
 | |
|             /* perhaps later we can add support for unzipping OS/2 EAs to NT */
 | |
|             case EF_OS2:
 | |
|                 rc = SetEAs(__G__ path, ef_ptr);
 | |
|                 break;
 | |
| 
 | |
|             case EF_IZUNIX:
 | |
|             case EF_IZUNIX2:
 | |
|             case EF_TIME:
 | |
|                 break;          /* handled elsewhere */
 | |
| #else /* ! 0 */
 | |
| #ifdef DEBUG
 | |
|             case EF_AV:
 | |
|             case EF_OS2:
 | |
|             case EF_PKVMS:
 | |
|             case EF_PKUNIX:
 | |
|             case EF_IZVMS:
 | |
|             case EF_IZUNIX:
 | |
|             case EF_IZUNIX2:
 | |
|             case EF_TIME:
 | |
|             case EF_JLMAC:
 | |
|             case EF_ZIPIT:
 | |
|             case EF_VMCMS:
 | |
|             case EF_MVS:
 | |
|             case EF_ACL:
 | |
|             case EF_BEOS:
 | |
|             case EF_QDOS:
 | |
|             case EF_AOSVS:
 | |
|             case EF_SPARK:
 | |
|             case EF_MD5:
 | |
|             case EF_ASIUNIX:
 | |
|                 break;          /* shut up for other known e.f. blocks  */
 | |
| #endif /* DEBUG */
 | |
| #endif /* ? 0 */
 | |
| 
 | |
|             default:
 | |
|                 Trace((stderr,
 | |
|                   "EvalExtraFields: unknown extra field block, ID=%u\n",
 | |
|                   eb_id));
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         ef_ptr += (eb_len + EB_HEADSIZE);
 | |
|         ef_len -= (eb_len + EB_HEADSIZE);
 | |
| 
 | |
|         if (rc != PK_OK)
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| #endif /* NTSD_EAS */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************/
 | |
| /* Function IsWinNT() */
 | |
| /**********************/
 | |
| 
 | |
| int IsWinNT(void)       /* returns TRUE if real NT, FALSE if Win95 or Win32s */
 | |
| {
 | |
|     static DWORD g_PlatformId = 0xFFFFFFFF; /* saved platform indicator */
 | |
| 
 | |
|     if (g_PlatformId == 0xFFFFFFFF) {
 | |
|         /* note: GetVersionEx() doesn't exist on WinNT 3.1 */
 | |
|         if (GetVersion() < 0x80000000)
 | |
|             g_PlatformId = TRUE;
 | |
|         else
 | |
|             g_PlatformId = FALSE;
 | |
|     }
 | |
|     return (int)g_PlatformId;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifndef SFX
 | |
| 
 | |
| /************************/   /* can return PK_OK, PK_ERR, PK_MEM3, PK_MEM4, */
 | |
| /*  Function test_NT()  */   /*  IZ_EF_TRUNC, or (if testing) PK_ERR or'd */
 | |
| /************************/   /*  with compression method in high byte */
 | |
| 
 | |
| int test_NT(__G__ eb, eb_size)
 | |
|     __GDEF
 | |
|     uch *eb;
 | |
|     unsigned eb_size;
 | |
| {
 | |
|     ulg eb_ucsize = makelong(eb + (EB_HEADSIZE+EB_NTSD_LSIZE));
 | |
|     uch *eb_uncompressed;
 | |
|     int r;
 | |
| 
 | |
|     if (eb_ucsize > 0L && eb_size <= (EB_NTSD_L_LEN + 6))
 | |
|         return IZ_EF_TRUNC;             /* no compressed data! */
 | |
| 
 | |
|     if ((eb_uncompressed = (uch *)malloc((extent)eb_ucsize)) == (uch *)NULL)
 | |
|         return PK_MEM4;
 | |
| 
 | |
|     r = memextract(__G__ eb_uncompressed, eb_ucsize, (eb + EB_NTSD_L_LEN),
 | |
|                    (ulg)(eb_size - (EB_NTSD_L_LEN - EB_HEADSIZE)));
 | |
| 
 | |
| #ifndef NO_NTSD_WITH_RSXNT
 | |
|     if (r == PK_OK  &&  !ValidateSecurity(eb_uncompressed))
 | |
|         r = PK_ERR;
 | |
| #endif
 | |
| 
 | |
|     free(eb_uncompressed);
 | |
|     return r;
 | |
| 
 | |
| } /* end function test_NT() */
 | |
| 
 | |
| #endif /* !SFX */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
 | |
| 
 | |
| /*****************************/
 | |
| /* Function utime2FileTime() */     /* convert Unix time_t format into the */
 | |
| /*****************************/     /* form used by SetFileTime() in NT/95 */
 | |
| 
 | |
| #define UNIX_TIME_ZERO_HI  0x019DB1DE
 | |
| #define UNIX_TIME_ZERO_LO  0xD53E8000
 | |
| #define NT_QUANTA_PER_UNIX 10000000
 | |
| 
 | |
| static void utime2FileTime(time_t ut, FILETIME *pft)
 | |
| {
 | |
| #if defined(__GNUC__) || defined(ULONG_LONG_MAX)
 | |
|     unsigned long long NTtime;
 | |
| 
 | |
|     NTtime = ((unsigned long long)ut * NT_QUANTA_PER_UNIX) +
 | |
|              ((unsigned long long)UNIX_TIME_ZERO_LO +
 | |
|               ((unsigned long long)UNIX_TIME_ZERO_HI << 32));
 | |
|     pft->dwLowDateTime = (DWORD)NTtime;
 | |
|     pft->dwHighDateTime = (DWORD)(NTtime >> 32);
 | |
| 
 | |
| #else /* "unsigned long long" may not be supported */
 | |
|     unsigned int b1, b2, carry = 0;
 | |
|     unsigned long r0, r1, r2, r3, r4;
 | |
| 
 | |
|     b1 = ut & 0xFFFF;
 | |
|     b2 = (ut >> 16) & 0xFFFF;       /* if ut is over 32 bits, too bad */
 | |
|     r1 = b1 * (NT_QUANTA_PER_UNIX & 0xFFFF);
 | |
|     r2 = b1 * (NT_QUANTA_PER_UNIX >> 16);
 | |
|     r3 = b2 * (NT_QUANTA_PER_UNIX & 0xFFFF);
 | |
|     r4 = b2 * (NT_QUANTA_PER_UNIX >> 16);
 | |
|     r0 = (r1 + (r2 << 16)) & 0xFFFFFFFF;
 | |
|     if (r0 < r1)
 | |
|         carry++;
 | |
|     r1 = r0;
 | |
|     r0 = (r0 + (r3 << 16)) & 0xFFFFFFFF;
 | |
|     if (r0 < r1)
 | |
|         carry++;
 | |
|     pft->dwLowDateTime = r0 + UNIX_TIME_ZERO_LO;
 | |
|     if (pft->dwLowDateTime < r0)
 | |
|         carry++;
 | |
|     pft->dwHighDateTime = r4 + (r2 >> 16) + (r3 >> 16)
 | |
|                             + UNIX_TIME_ZERO_HI + carry;
 | |
| #endif /* ?(64-bit "unsigned long long" support) */
 | |
| 
 | |
| } /* end function utime2FileTime() */
 | |
| 
 | |
| 
 | |
| 
 | |
| /******************************/
 | |
| /* Function NTQueryTargetFS() */
 | |
| /******************************/
 | |
| 
 | |
| static int NTQueryTargetFS(char *path)
 | |
| {
 | |
|     char     *tmp0;
 | |
|     char      rootPathName[4];
 | |
|     char      tmp1[MAX_PATH], tmp2[MAX_PATH];
 | |
|     unsigned  volSerNo, maxCompLen, fileSysFlags;
 | |
| #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
 | |
|     char *ansi_path = (char *)alloca(strlen(path) + 1);
 | |
| 
 | |
|     INTERN_TO_ISO(path, ansi_path);
 | |
|     path = ansi_path;
 | |
| #endif
 | |
| 
 | |
|     if (isalpha(path[0]) && (path[1] == ':'))
 | |
|         tmp0 = path;
 | |
|     else
 | |
|     {
 | |
|         GetFullPathName(path, MAX_PATH, tmp1, &tmp0);
 | |
|         tmp0 = &tmp1[0];
 | |
|     }
 | |
|     strncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
 | |
|     rootPathName[3] = '\0';           /* e.g. "A:/"                */
 | |
| 
 | |
|     GetVolumeInformation((LPCTSTR)rootPathName, (LPTSTR)tmp1, (DWORD)MAX_PATH,
 | |
|                          (LPDWORD)&volSerNo, (LPDWORD)&maxCompLen,
 | |
|                          (LPDWORD)&fileSysFlags, (LPTSTR)tmp2, (DWORD)MAX_PATH);
 | |
| 
 | |
|     /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
 | |
|      * local time!
 | |
|      */
 | |
|     return !strncmp(strupr(tmp2), "FAT", 3) ||
 | |
|            !strncmp(tmp2, "VFAT", 4) ||
 | |
|            !strncmp(tmp2, "HPFS", 4);
 | |
| 
 | |
| } /* end function NTQueryTargetFS() */
 | |
| 
 | |
| #endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND */
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifndef NT_TZBUG_WORKAROUND
 | |
| #  define UTIME_BOUNDCHECK_1(utimval) \
 | |
|      if (fs_uses_loctime) { \
 | |
|          utime_dosmin = dos_to_unix_time(0x0021, 0); \
 | |
|          if ((ulg)utimval < (ulg)utime_dosmin) \
 | |
|              utimval = utime_dosmin; \
 | |
|      }
 | |
| #  define UTIME_BOUNDCHECK_N(utimval) \
 | |
|      if (fs_uses_loctime && ((ulg)utimval < (ulg)utime_dosmin)) \
 | |
|          utimval = utime_dosmin;
 | |
| #  define NT_TZBUG_PRECOMPENSATE(ut, pft)
 | |
| 
 | |
| #else
 | |
| #  define UTIME_1980_JAN_01_00_00   315532800L
 | |
| #  define UTIME_BOUNDCHECK_1(utimval)
 | |
| #  define UTIME_BOUNDCHECK_N(utimval)
 | |
| #  define NT_TZBUG_PRECOMPENSATE(ut, pft) \
 | |
|      if (fs_uses_loctime) NTtzbugWorkaround(ut, pft);
 | |
| 
 | |
|    /* nonzero if `y' is a leap year, else zero */
 | |
| #  define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
 | |
|    /* number of leap years from 1970 to `y' (not including `y' itself) */
 | |
| #  define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
 | |
| 
 | |
| extern ZCONST ush ydays[];
 | |
| 
 | |
| /********************************/
 | |
| /* Function UTCtime2Localtime() */   /* borrowed from Zip's mkgmtime() */
 | |
| /********************************/
 | |
| 
 | |
| static time_t UTCtime2Localtime(time_t utctime)
 | |
| {
 | |
|     time_t utc = utctime;
 | |
|     struct tm *tm;
 | |
|     unsigned years, months, days, hours, minutes, seconds;
 | |
| 
 | |
| 
 | |
| #ifdef __BORLANDC__   /* Borland C++ 5.x crashes when trying to reference tm */
 | |
|     if (utc < UTIME_1980_JAN_01_00_00)
 | |
|         utc = UTIME_1980_JAN_01_00_00;
 | |
| #endif
 | |
|     tm = localtime(&utc);
 | |
| 
 | |
|     years = tm->tm_year + 1900;  /* year - 1900 -> year */
 | |
|     months = tm->tm_mon;         /* 0..11 */
 | |
|     days = tm->tm_mday - 1;      /* 1..31 -> 0..30 */
 | |
|     hours = tm->tm_hour;         /* 0..23 */
 | |
|     minutes = tm->tm_min;        /* 0..59 */
 | |
|     seconds = tm->tm_sec;        /* 0..61 in ANSI C */
 | |
| 
 | |
|     /* set `days' to the number of days into the year */
 | |
|     days += ydays[months] + (months > 1 && leap(years));
 | |
| 
 | |
|     /* now set `days' to the number of days since 1 Jan 1970 */
 | |
|     days += 365 * (years - 1970) + nleap(years);
 | |
| 
 | |
|     return (time_t)(86400L * (ulg)days + 3600L * (ulg)hours +
 | |
|                     (ulg)(60 * minutes + seconds));
 | |
| 
 | |
| } /* end function UTCtime2Localtime() */
 | |
| 
 | |
| 
 | |
| 
 | |
| /********************************/
 | |
| /* Function NTtzbugWorkaround() */
 | |
| /********************************/
 | |
| 
 | |
| static void NTtzbugWorkaround(time_t ut, FILETIME *pft)
 | |
| {
 | |
|     FILETIME C_RTL_locft, NTAPI_locft;
 | |
|     time_t ux_loctime = UTCtime2Localtime(ut);
 | |
| 
 | |
|     /* This routine is only used when the target file system stores time-
 | |
|      * stamps as local time in MSDOS format.  Thus we make sure that the
 | |
|      * resulting timestamp is within the range of MSDOS date-time values. */
 | |
|     if (ux_loctime < UTIME_1980_JAN_01_00_00)
 | |
|         ux_loctime = UTIME_1980_JAN_01_00_00;
 | |
| 
 | |
|     utime2FileTime(ux_loctime, &C_RTL_locft);
 | |
|     if (!FileTimeToLocalFileTime(pft, &NTAPI_locft))
 | |
|         return;
 | |
|     else {
 | |
|         long time_shift_l, time_shift_h;
 | |
|         int carry = 0;
 | |
| 
 | |
|         time_shift_l = C_RTL_locft.dwLowDateTime - NTAPI_locft.dwLowDateTime;
 | |
|         if (C_RTL_locft.dwLowDateTime < NTAPI_locft.dwLowDateTime)
 | |
|             carry--;
 | |
|         time_shift_h = C_RTL_locft.dwHighDateTime - NTAPI_locft.dwHighDateTime;
 | |
|         pft->dwLowDateTime += time_shift_l;
 | |
|         if (pft->dwLowDateTime < (ulg)time_shift_l)
 | |
|             carry++;
 | |
|         pft->dwHighDateTime += time_shift_h + carry;
 | |
|     }
 | |
| } /* end function NTtzbugWorkaround() */
 | |
| 
 | |
| #endif /* ?NT_TZBUG_WORKAROUND */
 | |
| 
 | |
| 
 | |
| 
 | |
| /****************************/      /* Get the file time in a format that */
 | |
| /* Function getNTfiletime() */      /*  can be used by SetFileTime() in NT */
 | |
| /****************************/
 | |
| 
 | |
| static int getNTfiletime(__G__ pModFT, pAccFT, pCreFT)
 | |
|     __GDEF
 | |
|     FILETIME *pModFT;
 | |
|     FILETIME *pAccFT;
 | |
|     FILETIME *pCreFT;
 | |
| {
 | |
| #ifdef NT_TZBUG_WORKAROUND
 | |
|     time_t ux_modtime;
 | |
| #else /* !NT_TZBUG_WORKAROUND */
 | |
|     FILETIME locft;    /* 64-bit value made up of two 32-bit [low & high] */
 | |
|     WORD wDOSDate;     /* for converting from DOS date to Windows NT */
 | |
|     WORD wDOSTime;
 | |
| #endif /* ?NT_TZBUG_WORKAROUND */
 | |
| #ifdef USE_EF_UT_TIME
 | |
|     unsigned eb_izux_flg;
 | |
|     iztimes z_utime;   /* struct for Unix-style actime & modtime, + creatime */
 | |
| #endif
 | |
| #if (defined(USE_EF_UT_TIME) && !defined(NT_TZBUG_WORKAROUND))
 | |
|     time_t utime_dosmin;
 | |
| # endif
 | |
| #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
 | |
|     int fs_uses_loctime = NTQueryTargetFS(G.filename);
 | |
| #endif
 | |
| 
 | |
|     /* Copy and/or convert time and date variables, if necessary;
 | |
|      * return a flag indicating which time stamps are available. */
 | |
| #ifdef USE_EF_UT_TIME
 | |
|     if (G.extra_field && ((eb_izux_flg = ef_scan_for_izux(G.extra_field,
 | |
|         G.lrec.extra_field_length, 0, &z_utime, NULL)) & EB_UT_FL_MTIME))
 | |
|     {
 | |
|         TTrace((stderr, "getNTfiletime:  Unix e.f. modif. time = %lu\n",
 | |
|           z_utime.mtime));
 | |
|         UTIME_BOUNDCHECK_1(z_utime.mtime)
 | |
|         utime2FileTime(z_utime.mtime, pModFT);
 | |
|         NT_TZBUG_PRECOMPENSATE(z_utime.mtime, pModFT)
 | |
|         if (eb_izux_flg & EB_UT_FL_ATIME) {
 | |
|             UTIME_BOUNDCHECK_N(z_utime.atime)
 | |
|             utime2FileTime(z_utime.atime, pAccFT);
 | |
|             NT_TZBUG_PRECOMPENSATE(z_utime.atime, pAccFT)
 | |
|         }
 | |
|         if (eb_izux_flg & EB_UT_FL_CTIME) {
 | |
|             UTIME_BOUNDCHECK_N(z_utime.ctime)
 | |
|             utime2FileTime(z_utime.ctime, pCreFT);
 | |
|             NT_TZBUG_PRECOMPENSATE(z_utime.ctime, pCreFT)
 | |
|         }
 | |
|         return (int)eb_izux_flg;
 | |
|     }
 | |
| #endif /* USE_EF_UT_TIME */
 | |
| #ifdef NT_TZBUG_WORKAROUND
 | |
|     ux_modtime = dos_to_unix_time(G.lrec.last_mod_file_date,
 | |
|                                   G.lrec.last_mod_file_time);
 | |
|     utime2FileTime(ux_modtime, pModFT);
 | |
|     NT_TZBUG_PRECOMPENSATE(ux_modtime, pModFT)
 | |
| #else /* !NT_TZBUG_WORKAROUND */
 | |
| 
 | |
|     wDOSTime = (WORD)G.lrec.last_mod_file_time;
 | |
|     wDOSDate = (WORD)G.lrec.last_mod_file_date;
 | |
| 
 | |
|     /* The DosDateTimeToFileTime() function converts a DOS date/time
 | |
|      * into a 64-bit Windows NT file time */
 | |
|     if (!DosDateTimeToFileTime(wDOSDate, wDOSTime, &locft))
 | |
|     {
 | |
|         Info(slide, 0, ((char *)slide, "DosDateTime failed: %d\n",
 | |
|           (int)GetLastError()));
 | |
|         return 0;
 | |
|     }
 | |
|     if (!LocalFileTimeToFileTime(&locft, pModFT))
 | |
|     {
 | |
|         Info(slide, 0, ((char *)slide, "LocalFileTime failed: %d\n",
 | |
|           (int)GetLastError()));
 | |
|         *pModFT = locft;
 | |
|     }
 | |
| #endif /* ?NT_TZBUG_WORKAROUND */
 | |
|     *pAccFT = *pModFT;
 | |
|     return (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
 | |
| 
 | |
| } /* end function getNTfiletime() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /****************************/
 | |
| /* Function close_outfile() */
 | |
| /****************************/
 | |
| 
 | |
| void close_outfile(__G)
 | |
|     __GDEF
 | |
| {
 | |
|     FILETIME Modft;    /* File time type defined in NT, `last modified' time */
 | |
|     FILETIME Accft;    /* NT file time type, `last access' time */
 | |
|     FILETIME Creft;    /* NT file time type, `file creation' time */
 | |
|     HANDLE hFile;      /* File handle defined in NT    */
 | |
|     int gotTime;
 | |
| #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
 | |
|     char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
 | |
| 
 | |
|     INTERN_TO_ISO(G.filename, ansi_name);
 | |
| #   define Ansi_Fname  ansi_name
 | |
| #else
 | |
| #   define Ansi_Fname  G.filename
 | |
| #endif
 | |
| 
 | |
|     /* don't set the time stamp on standard output */
 | |
|     if (G.cflag) {
 | |
|         fclose(G.outfile);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     gotTime = getNTfiletime(__G__ &Modft, &Accft, &Creft);
 | |
| 
 | |
|     /* Close the file and then re-open it using the Win32
 | |
|      * CreateFile call, so that the file can be created
 | |
|      * with GENERIC_WRITE access, otherwise the SetFileTime
 | |
|      * call will fail. */
 | |
|     fclose(G.outfile);
 | |
| 
 | |
|     /* open a handle to the file before processing extra fields;
 | |
|        we do this in case new security on file prevents us from updating
 | |
|        time stamps */
 | |
|     hFile = CreateFile(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
 | |
|          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|     /* sfield@microsoft.com: set attributes before time in case we decide to
 | |
|        support other filetime members later.  This also allows us to apply
 | |
|        attributes before the security is changed, which may prevent this
 | |
|        from succeeding otherwise.  Also, since most files don't have
 | |
|        any interesting attributes, only change them if something other than
 | |
|        FILE_ATTRIBUTE_ARCHIVE appears in the attributes.  This works well
 | |
|        as an optimization because FILE_ATTRIBUTE_ARCHIVE gets applied to the
 | |
|        file anyway, when it's created new. */
 | |
|     if((G.pInfo->file_attr & 0x7F) & ~FILE_ATTRIBUTE_ARCHIVE) {
 | |
|         if (!SetFileAttributes(Ansi_Fname, G.pInfo->file_attr & 0x7F))
 | |
|             Info(slide, 1, ((char *)slide,
 | |
|               "\nwarning (%d): could not set file attributes\n",
 | |
|               (int)GetLastError()));
 | |
|     }
 | |
| 
 | |
| #ifdef NTSD_EAS
 | |
|     /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
 | |
|     if (G.extra_field) {    /* zipfile extra field may have extended attribs */
 | |
|         int err = EvalExtraFields(__G__ G.filename, G.extra_field,
 | |
|                                   G.lrec.extra_field_length);
 | |
| 
 | |
|         if (err == IZ_EF_TRUNC) {
 | |
|             if (G.qflag)
 | |
|                 Info(slide, 1, ((char *)slide, "%-22s ",
 | |
|                   FnFilter1(G.filename)));
 | |
|             Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
 | |
|               makeword(G.extra_field+2)-10, G.qflag? "\n":""));
 | |
|         }
 | |
|     }
 | |
| #endif /* NTSD_EAS */
 | |
| 
 | |
|     if ( hFile == INVALID_HANDLE_VALUE )
 | |
|         Info(slide, 1, ((char *)slide,
 | |
|           "\nCreateFile error %d when trying set file time\n",
 | |
|           (int)GetLastError()));
 | |
|     else {
 | |
|         if (gotTime) {
 | |
|             FILETIME *pModft = (gotTime & EB_UT_FL_MTIME) ? &Modft : NULL;
 | |
|             FILETIME *pAccft = (gotTime & EB_UT_FL_ATIME) ? &Accft : NULL;
 | |
|             FILETIME *pCreft = (gotTime & EB_UT_FL_CTIME) ? &Creft : NULL;
 | |
| 
 | |
|             if (!SetFileTime(hFile, pCreft, pAccft, pModft))
 | |
|                 Info(slide, 0, ((char *)slide, "\nSetFileTime failed: %d\n",
 | |
|                   (int)GetLastError()));
 | |
|         }
 | |
|         CloseHandle(hFile);
 | |
|     }
 | |
| 
 | |
|     return;
 | |
| 
 | |
| #undef Ansi_Fname
 | |
| 
 | |
| } /* end function close_outfile() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /***********************/
 | |
| /* Function isfloppy() */   /* more precisely, is it removable? */
 | |
| /***********************/
 | |
| 
 | |
| static int isfloppy(int nDrive)   /* 1 == A:, 2 == B:, etc. */
 | |
| {
 | |
|     char rootPathName[4];
 | |
| 
 | |
|     rootPathName[0] = (char)('A' + nDrive - 1);   /* build the root path */
 | |
|     rootPathName[1] = ':';                        /*  name, e.g. "A:/" */
 | |
|     rootPathName[2] = '/';
 | |
|     rootPathName[3] = '\0';
 | |
| 
 | |
|     return (GetDriveType(rootPathName) == DRIVE_REMOVABLE);
 | |
| 
 | |
| } /* end function isfloppy() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /*****************************/
 | |
| /* Function IsVolumeOldFAT() */
 | |
| /*****************************/
 | |
| 
 | |
| /*
 | |
|  * Note:  8.3 limits on filenames apply only to old-style FAT filesystems.
 | |
|  *        More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
 | |
|  *        can support long filenames (LFN) on FAT filesystems.  Check the
 | |
|  *        filesystem maximum component length field to detect LFN support.
 | |
|  *        [GRR:  this routine is only used to determine whether spaces in
 | |
|  *        filenames are supported...]
 | |
|  */
 | |
| 
 | |
| static int IsVolumeOldFAT(char *name)
 | |
| {
 | |
|     char     *tmp0;
 | |
|     char      rootPathName[4];
 | |
|     char      tmp1[MAX_PATH], tmp2[MAX_PATH];
 | |
|     unsigned  volSerNo, maxCompLen, fileSysFlags;
 | |
| #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
 | |
|     char *ansi_name = (char *)alloca(strlen(name) + 1);
 | |
| 
 | |
|     INTERN_TO_ISO(name, ansi_name);
 | |
|     name = ansi_name;
 | |
| #endif
 | |
| 
 | |
|     if (isalpha(name[0]) && (name[1] == ':'))
 | |
|         tmp0 = name;
 | |
|     else
 | |
|     {
 | |
|         GetFullPathName(name, MAX_PATH, tmp1, &tmp0);
 | |
|         tmp0 = &tmp1[0];
 | |
|     }
 | |
|     strncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
 | |
|     rootPathName[3] = '\0';           /* e.g. "A:/"                */
 | |
| 
 | |
|     GetVolumeInformation((LPCTSTR)rootPathName, (LPTSTR)tmp1, (DWORD)MAX_PATH,
 | |
|                          (LPDWORD)&volSerNo, (LPDWORD)&maxCompLen,
 | |
|                          (LPDWORD)&fileSysFlags, (LPTSTR)tmp2, (DWORD)MAX_PATH);
 | |
| 
 | |
|     /* Long Filenames (LFNs) are available if the component length is > 12 */
 | |
|     return maxCompLen <= 12;
 | |
| /*  return !strncmp(strupr(tmp2), "FAT", 3);   old version */
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /******************************/
 | |
| /* Function IsFileNameValid() */
 | |
| /******************************/
 | |
| 
 | |
| static int IsFileNameValid(char *name)
 | |
| {
 | |
|     HFILE    hf;
 | |
|     OFSTRUCT of;
 | |
| #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
 | |
|     char *ansi_name = (char *)alloca(strlen(name) + 1);
 | |
| 
 | |
|     INTERN_TO_ISO(name, ansi_name);
 | |
|     name = ansi_name;
 | |
| #endif
 | |
| 
 | |
|     hf = OpenFile(name, &of, OF_READ | OF_SHARE_DENY_NONE);
 | |
|     if (hf == HFILE_ERROR)
 | |
|         switch (GetLastError())
 | |
|         {
 | |
|             case ERROR_INVALID_NAME:
 | |
|             case ERROR_FILENAME_EXCED_RANGE:
 | |
|                 return FALSE;
 | |
|             default:
 | |
|                 return TRUE;
 | |
|         }
 | |
|     else
 | |
|         _lclose(hf);
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifndef SFX
 | |
| 
 | |
| /************************/
 | |
| /*  Function do_wild()  */   /* identical to OS/2 version */
 | |
| /************************/
 | |
| 
 | |
| char *do_wild(__G__ wildspec)
 | |
|     __GDEF
 | |
|     char *wildspec;         /* only used first time on a given dir */
 | |
| {
 | |
|  /* static zDIR *dir = NULL;                               */
 | |
|  /* static char *dirname, *wildname, matchname[FILNAMSIZ]; */
 | |
|  /* static int firstcall=TRUE, have_dirname, dirnamelen;   */
 | |
|     struct zdirent *file;
 | |
| 
 | |
|     /* Even when we're just returning wildspec, we *always* do so in
 | |
|      * matchname[]--calling routine is allowed to append four characters
 | |
|      * to the returned string, and wildspec may be a pointer to argv[].
 | |
|      */
 | |
|     if (!G.notfirstcall) {  /* first call:  must initialize everything */
 | |
|         G.notfirstcall = TRUE;
 | |
| 
 | |
|         if (!iswild(wildspec)) {
 | |
|             strcpy(G.matchname, wildspec);
 | |
|             G.have_dirname = FALSE;
 | |
|             G.wild_dir = NULL;
 | |
|             return G.matchname;
 | |
|         }
 | |
| 
 | |
|         /* break the wildspec into a directory part and a wildcard filename */
 | |
|         if ((G.wildname = strrchr(wildspec, '/')) == NULL &&
 | |
|             (G.wildname = strrchr(wildspec, ':')) == NULL) {
 | |
|             G.dirname = ".";
 | |
|             G.dirnamelen = 1;
 | |
|             G.have_dirname = FALSE;
 | |
|             G.wildname = wildspec;
 | |
|         } else {
 | |
|             ++G.wildname;     /* point at character after '/' or ':' */
 | |
|             G.dirnamelen = G.wildname - wildspec;
 | |
|             if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == NULL) {
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "warning:  can't allocate wildcard buffers\n"));
 | |
|                 strcpy(G.matchname, wildspec);
 | |
|                 return G.matchname; /* but maybe filespec was not a wildcard */
 | |
|             }
 | |
|             strncpy(G.dirname, wildspec, G.dirnamelen);
 | |
|             G.dirname[G.dirnamelen] = '\0';    /* terminate for strcpy below */
 | |
|             G.have_dirname = TRUE;
 | |
|         }
 | |
|         Trace((stderr, "do_wild:  dirname = [%s]\n", G.dirname));
 | |
| 
 | |
|         if ((G.wild_dir = (zvoid *)Opendir(G.dirname)) != NULL) {
 | |
|             while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
 | |
|                 Trace((stderr, "do_wild:  Readdir returns %s\n", file->d_name));
 | |
|                 if (match(file->d_name, G.wildname, 1)) { /* 1 == ignore case */
 | |
|                     Trace((stderr, "do_wild:  match() succeeds\n"));
 | |
|                     if (G.have_dirname) {
 | |
|                         strcpy(G.matchname, G.dirname);
 | |
|                         strcpy(G.matchname+G.dirnamelen, file->d_name);
 | |
|                     } else
 | |
|                         strcpy(G.matchname, file->d_name);
 | |
|                     return G.matchname;
 | |
|                 }
 | |
|             }
 | |
|             /* if we get to here directory is exhausted, so close it */
 | |
|             Closedir((zDIR *)G.wild_dir);
 | |
|             G.wild_dir = NULL;
 | |
|         }
 | |
|         Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n", G.dirname));
 | |
| 
 | |
|         /* return the raw wildspec in case that works (e.g., directory not
 | |
|          * searchable, but filespec was not wild and file is readable) */
 | |
|         strcpy(G.matchname, wildspec);
 | |
|         return G.matchname;
 | |
|     }
 | |
| 
 | |
|     /* last time through, might have failed opendir but returned raw wildspec */
 | |
|     if (G.wild_dir == NULL) {
 | |
|         G.notfirstcall = FALSE;    /* reset for new wildspec */
 | |
|         if (G.have_dirname)
 | |
|             free(G.dirname);
 | |
|         return (char *)NULL;
 | |
|     }
 | |
| 
 | |
|     /* If we've gotten this far, we've read and matched at least one entry
 | |
|      * successfully (in a previous call), so dirname has been copied into
 | |
|      * matchname already.
 | |
|      */
 | |
|     while ((file = Readdir((zDIR *)G.wild_dir)) != NULL)
 | |
|         if (match(file->d_name, G.wildname, 1)) {   /* 1 == ignore case */
 | |
|             if (G.have_dirname) {
 | |
|                 /* strcpy(G.matchname, G.dirname); */
 | |
|                 strcpy(G.matchname+G.dirnamelen, file->d_name);
 | |
|             } else
 | |
|                 strcpy(G.matchname, file->d_name);
 | |
|             return G.matchname;
 | |
|         }
 | |
| 
 | |
|     Closedir((zDIR *)G.wild_dir);  /* at least one entry read; nothing left */
 | |
|     G.wild_dir = NULL;
 | |
|     G.notfirstcall = FALSE;        /* reset for new wildspec */
 | |
|     if (G.have_dirname)
 | |
|         free(G.dirname);
 | |
|     return (char *)NULL;
 | |
| 
 | |
| } /* end function do_wild() */
 | |
| 
 | |
| #endif /* !SFX */
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************/
 | |
| /* Function mapattr() */
 | |
| /**********************/
 | |
| 
 | |
| /* Identical to MS-DOS, OS/2 versions.  However, NT has a lot of extra
 | |
|  * permission stuff, so this function should probably be extended in the
 | |
|  * future. */
 | |
| 
 | |
| int mapattr(__G)
 | |
|     __GDEF
 | |
| {
 | |
|     /* set archive bit (file is not backed up): */
 | |
|     G.pInfo->file_attr = (unsigned)(G.crec.external_file_attributes | FILE_ATTRIBUTE_ARCHIVE) &
 | |
|       0xff;
 | |
|     return 0;
 | |
| 
 | |
| } /* end function mapattr() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************/
 | |
| /*  Function mapname()  */
 | |
| /************************/
 | |
|                              /* return 0 if no error, 1 if caution (filename */
 | |
| int mapname(__G__ renamed)   /*  truncated), 2 if warning (skip file because */
 | |
|     __GDEF                   /*  dir doesn't exist), 3 if error (skip file), */
 | |
|     int renamed;             /*  or 10 if out of memory (skip file) */
 | |
| {                            /*  [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
 | |
|     char pathcomp[FILNAMSIZ];   /* path-component buffer */
 | |
|     char *pp, *cp=NULL;         /* character pointers */
 | |
|     char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
 | |
|     int error;
 | |
|     register unsigned workch;   /* hold the character being tested */
 | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     Initialize various pointers and counters and stuff.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     /* can create path as long as not just freshening, or if user told us */
 | |
|     G.create_dirs = (!G.fflag || renamed);
 | |
| 
 | |
|     G.created_dir = FALSE;      /* not yet */
 | |
|     G.renamed_fullpath = FALSE;
 | |
|     G.fnlen = strlen(G.filename);
 | |
| 
 | |
|     if (renamed) {
 | |
|         cp = G.filename - 1;    /* point to beginning of renamed name... */
 | |
|         while (*++cp)
 | |
|             if (*cp == '\\')    /* convert backslashes to forward */
 | |
|                 *cp = '/';
 | |
|         cp = G.filename;
 | |
|         /* use temporary rootpath if user gave full pathname */
 | |
|         if (G.filename[0] == '/') {
 | |
|             G.renamed_fullpath = TRUE;
 | |
|             pathcomp[0] = '/';  /* copy the '/' and terminate */
 | |
|             pathcomp[1] = '\0';
 | |
|             ++cp;
 | |
|         } else if (isalpha(G.filename[0]) && G.filename[1] == ':') {
 | |
|             G.renamed_fullpath = TRUE;
 | |
|             pp = pathcomp;
 | |
|             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
 | |
|             *pp++ = *cp++;
 | |
|             if (*cp == '/')
 | |
|                 *pp++ = *cp++;  /* otherwise add "./"? */
 | |
|             *pp = '\0';
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
 | |
|     if ((error = checkdir(__G__ pathcomp, INIT)) != 0)    /* init path buffer */
 | |
|         return error;           /* ...unless no mem or vol label on hard disk */
 | |
| 
 | |
|     *pathcomp = '\0';           /* initialize translation buffer */
 | |
|     pp = pathcomp;              /* point to translation buffer */
 | |
|     if (!renamed) {             /* cp already set if renamed */
 | |
|         if (G.jflag)            /* junking directories */
 | |
|             cp = (char *)strrchr(G.filename, '/');
 | |
|         if (cp == NULL)         /* no '/' or not junking dirs */
 | |
|             cp = G.filename;    /* point to internal zipfile-member pathname */
 | |
|         else
 | |
|             ++cp;               /* point to start of last component of path */
 | |
|     }
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     Begin main loop through characters in filename.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     while ((workch = (uch)*cp++) != 0) {
 | |
| 
 | |
|         switch (workch) {
 | |
|         case '/':             /* can assume -j flag not given */
 | |
|             *pp = '\0';
 | |
|             if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
 | |
|                 return error;
 | |
|             pp = pathcomp;    /* reset conversion buffer for next piece */
 | |
|             lastsemi = NULL;  /* leave directory semi-colons alone */
 | |
|             break;
 | |
| 
 | |
|         case ':':             /* drive names not stored in zipfile, */
 | |
|         case '<':             /*  so no colons allowed */
 | |
|         case '>':             /* no redirection symbols allowed either */
 | |
|         case '|':             /* no pipe signs allowed */
 | |
|         case '"':             /* no double quotes allowed */
 | |
|         case '?':             /* no wildcards allowed */
 | |
|         case '*':
 | |
|             *pp++ = '_';      /* these rules apply equally to FAT and NTFS */
 | |
|             break;
 | |
|         case ';':             /* start of VMS version? */
 | |
|             lastsemi = pp;    /* remove VMS version later... */
 | |
|             *pp++ = ';';      /*  but keep semicolon for now */
 | |
|             break;
 | |
| 
 | |
|         case ' ':             /* keep spaces unless specifically */
 | |
|             /* NT cannot create filenames with spaces on FAT volumes */
 | |
|             if (G.sflag || IsVolumeOldFAT(G.filename))
 | |
|                 *pp++ = '_';
 | |
|             else
 | |
|                 *pp++ = ' ';
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             /* allow European characters in filenames: */
 | |
|             if (isprint(workch) || workch >= 127)
 | |
|                 *pp++ = (char)workch;
 | |
|         } /* end switch */
 | |
|     } /* end while loop */
 | |
| 
 | |
|     *pp = '\0';                   /* done with pathcomp:  terminate it */
 | |
| 
 | |
|     /* if not saving them, remove VMS version numbers (appended "###") */
 | |
|     if (!G.V_flag && lastsemi) {
 | |
|         pp = lastsemi + 1;        /* semi-colon was kept:  expect #'s after */
 | |
|         while (isdigit((uch)(*pp)))
 | |
|             ++pp;
 | |
|         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
 | |
|             *lastsemi = '\0';
 | |
|     }
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     Report if directory was created (and no file to create:  filename ended
 | |
|     in '/'), check name to be sure it exists, and combine path and name be-
 | |
|     fore exiting.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (G.filename[G.fnlen-1] == '/') {
 | |
|         checkdir(__G__ G.filename, GETPATH);
 | |
|         if (G.created_dir) {
 | |
|             if (QCOND2) {
 | |
|                 Info(slide, 0, ((char *)slide, "   creating: %-22s\n",
 | |
|                   FnFilter1(G.filename)));
 | |
|             }
 | |
|             /* HG: are we setting the date & time on a newly created   */
 | |
|             /*     dir?  Not quite sure how to do this.  It does not   */
 | |
|             /*     seem to be done in the MS-DOS version of mapname(). */
 | |
| 
 | |
| #ifdef NTSD_EAS
 | |
|             /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
 | |
|             if (G.extra_field) { /* zipfile e.f. may have extended attribs */
 | |
|                 int err = EvalExtraFields(__G__ G.filename, G.extra_field,
 | |
|                                           G.lrec.extra_field_length);
 | |
| 
 | |
|                 if (err == IZ_EF_TRUNC) {
 | |
|                     if (G.qflag)
 | |
|                         Info(slide, 1, ((char *)slide, "%-22s ",
 | |
|                           FnFilter1(G.filename)));
 | |
|                     Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
 | |
|                       makeword(G.extra_field+2)-10, G.qflag? "\n":""));
 | |
|                 }
 | |
|             }
 | |
| #endif /* NTSD_EAS */
 | |
|             return IZ_CREATED_DIR;      /* set dir time (note trailing '/') */
 | |
|         }
 | |
|         return 2;   /* dir existed already; don't look for data to extract */
 | |
|     }
 | |
| 
 | |
|     if (*pathcomp == '\0') {
 | |
|         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
 | |
|           FnFilter1(G.filename)));
 | |
|         return 3;
 | |
|     }
 | |
| 
 | |
|     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
 | |
|     checkdir(__G__ G.filename, GETPATH);
 | |
|     Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
 | |
|       FnFilter1(G.filename), error));
 | |
| 
 | |
|     if (G.pInfo->vollabel) {    /* set the volume label now */
 | |
|         char drive[4];
 | |
| #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
 | |
|         char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
 | |
|         INTERN_TO_ISO(G.filename, ansi_name);
 | |
| #       define Ansi_Fname  ansi_name
 | |
| #else
 | |
| #       define Ansi_Fname  G.filename
 | |
| #endif
 | |
| 
 | |
|         /* Build a drive string, e.g. "b:" */
 | |
|         drive[0] = (char)('a' + G.nLabelDrive - 1);
 | |
|         strcpy(drive + 1, ":\\");
 | |
|         if (QCOND2)
 | |
|             Info(slide, 0, ((char *)slide, "labelling %s %-22s\n", drive,
 | |
|               FnFilter1(G.filename)));
 | |
|         if (!SetVolumeLabel(drive, Ansi_Fname)) {
 | |
|             Info(slide, 1, ((char *)slide,
 | |
|               "mapname:  error setting volume label\n"));
 | |
|             return 3;
 | |
|         }
 | |
|         return 2;   /* success:  skip the "extraction" quietly */
 | |
| #undef Ansi_Fname
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| 
 | |
| } /* end function mapname() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************/
 | |
| /* Function map2fat() */        /* Not quite identical to OS/2 version */
 | |
| /**********************/
 | |
| 
 | |
| static void map2fat(pathcomp, pEndFAT)
 | |
|     char *pathcomp, **pEndFAT;
 | |
| {
 | |
|     char *ppc = pathcomp;       /* variable pointer to pathcomp */
 | |
|     char *pEnd = *pEndFAT;      /* variable pointer to buildpathFAT */
 | |
|     char *pBegin = *pEndFAT;    /* constant pointer to start of this comp. */
 | |
|     char *last_dot = NULL;      /* last dot not converted to underscore */
 | |
|     int dotname = FALSE;        /* flag:  path component begins with dot */
 | |
|                                 /*  ("." and ".." don't count) */
 | |
|     register unsigned workch;   /* hold the character being tested */
 | |
| 
 | |
| 
 | |
|     /* Only need check those characters which are legal in NTFS but not
 | |
|      * in FAT:  to get here, must already have passed through mapname.
 | |
|      * Also must truncate path component to ensure 8.3 compliance.
 | |
|      */
 | |
|     while ((workch = (uch)*ppc++) != 0) {
 | |
|         switch (workch) {
 | |
|             case '[':
 | |
|             case ']':
 | |
|             case '+':
 | |
|             case ',':
 | |
|             case ';':
 | |
|             case '=':
 | |
|                 *pEnd++ = '_';      /* convert brackets to underscores */
 | |
|                 break;
 | |
| 
 | |
|             case '.':
 | |
|                 if (pEnd == *pEndFAT) {   /* nothing appended yet... */
 | |
|                     if (*ppc == '\0')     /* don't bother appending a */
 | |
|                         break;            /*  "./" component to the path */
 | |
|                     else if (*ppc == '.' && ppc[1] == '\0') {   /* "../" */
 | |
|                         *pEnd++ = '.';    /* add first dot, unchanged... */
 | |
|                         ++ppc;            /* skip second dot, since it will */
 | |
|                     } else {              /*  be "added" at end of if-block */
 | |
|                         *pEnd++ = '_';    /* FAT doesn't allow null filename */
 | |
|                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
 | |
|                     }                     /*  (extra '_' now, "dot" below) */
 | |
|                 } else if (dotname) {     /* found a second dot, but still */
 | |
|                     dotname = FALSE;      /*  have extra leading underscore: */
 | |
|                     *pEnd = '\0';         /*  remove it by shifting chars */
 | |
|                     pEnd = *pEndFAT + 1;  /*  left one space (e.g., .p1.p2: */
 | |
|                     while (pEnd[1]) {     /*  __p1 -> _p1_p2 -> _p1.p2 when */
 | |
|                         *pEnd = pEnd[1];  /*  finished) [opt.:  since first */
 | |
|                         ++pEnd;           /*  two chars are same, can start */
 | |
|                     }                     /*  shifting at second position] */
 | |
|                 }
 | |
|                 last_dot = pEnd;    /* point at last dot so far... */
 | |
|                 *pEnd++ = '_';      /* convert dot to underscore for now */
 | |
|                 break;
 | |
| 
 | |
|             default:
 | |
|                 *pEnd++ = (char)workch;
 | |
| 
 | |
|         } /* end switch */
 | |
|     } /* end while loop */
 | |
| 
 | |
|     *pEnd = '\0';                 /* terminate buildpathFAT */
 | |
| 
 | |
|     /* NOTE:  keep in mind that pEnd points to the end of the path
 | |
|      * component, and *pEndFAT still points to the *beginning* of it...
 | |
|      * Also note that the algorithm does not try to get too fancy:
 | |
|      * if there are no dots already, the name either gets truncated
 | |
|      * at 8 characters or the last underscore is converted to a dot
 | |
|      * (only if more characters are saved that way).  In no case is
 | |
|      * a dot inserted between existing characters.
 | |
|      */
 | |
|     if (last_dot == NULL) {       /* no dots:  check for underscores... */
 | |
|         char *plu = strrchr(pBegin, '_');   /* pointer to last underscore */
 | |
| 
 | |
|         if (plu == NULL) {   /* no dots, no underscores:  truncate at 8 chars */
 | |
|             *pEndFAT += 8;        /* (or could insert '.' and keep 11...?) */
 | |
|             if (*pEndFAT > pEnd)
 | |
|                 *pEndFAT = pEnd;  /* oops...didn't have 8 chars to truncate */
 | |
|             else
 | |
|                 **pEndFAT = '\0';
 | |
|         } else if (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8) {
 | |
|             last_dot = plu;       /* be lazy:  drop through to next if-blk */
 | |
|         } else if ((pEnd - *pEndFAT) > 8) {
 | |
|             *pEndFAT += 8;        /* more fits into just basename than if */
 | |
|             **pEndFAT = '\0';     /*  convert last underscore to dot */
 | |
|         } else
 | |
|             *pEndFAT = pEnd;      /* whole thing fits into 8 chars or less */
 | |
|     }
 | |
| 
 | |
|     if (last_dot != NULL) {       /* one dot (or two, in the case of */
 | |
|         *last_dot = '.';          /*  "..") is OK:  put it back in */
 | |
| 
 | |
|         if ((last_dot - pBegin) > 8) {
 | |
|             char *p=last_dot, *q=pBegin+8;
 | |
|             int i;
 | |
| 
 | |
|             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
 | |
|                 *q++ = *p++;                   /*  shift .ext left and trun- */
 | |
|             *q = '\0';                         /*  cate/terminate it */
 | |
|             *pEndFAT = q;
 | |
|         } else if ((pEnd - last_dot) > 4) {    /* too many chars in extension */
 | |
|             *pEndFAT = last_dot + 4;
 | |
|             **pEndFAT = '\0';
 | |
|         } else
 | |
|             *pEndFAT = pEnd;   /* filename is fine; point at terminating zero */
 | |
|     }
 | |
| } /* end function map2fat() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /***********************/       /* Borrowed from os2.c for UnZip 5.1.        */
 | |
| /* Function checkdir() */       /* Difference: no EA stuff                   */
 | |
| /***********************/       /*             HPFS stuff works on NTFS too  */
 | |
| 
 | |
| int checkdir(__G__ pathcomp, flag)
 | |
|     __GDEF
 | |
|     char *pathcomp;
 | |
|     int flag;
 | |
| /*
 | |
|  * returns:  1 - (on APPEND_NAME) truncated filename
 | |
|  *           2 - path doesn't exist, not allowed to create
 | |
|  *           3 - path doesn't exist, tried to create and failed; or
 | |
|  *               path exists and is not a directory, but is supposed to be
 | |
|  *           4 - path is too long
 | |
|  *          10 - can't allocate memory for filename buffers
 | |
|  */
 | |
| {
 | |
|  /* static int rootlen = 0;     */   /* length of rootpath */
 | |
|  /* static char *rootpath;      */   /* user's "extract-to" directory */
 | |
|  /* static char *buildpathHPFS; */   /* full path (so far) to extracted file, */
 | |
|  /* static char *buildpathFAT;  */   /*  both HPFS/EA (main) and FAT versions */
 | |
|  /* static char *endHPFS;       */   /* corresponding pointers to end of */
 | |
|  /* static char *endFAT;        */   /*  buildpath ('\0') */
 | |
| 
 | |
| #   define FN_MASK   7
 | |
| #   define FUNCTION  (flag & FN_MASK)
 | |
| 
 | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     APPEND_DIR:  append the path component to the path being built and check
 | |
|     for its existence.  If doesn't exist and we are creating directories, do
 | |
|     so for this one; else signal success or error as appropriate.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (FUNCTION == APPEND_DIR) {
 | |
|         char *p = pathcomp;
 | |
|         int too_long=FALSE;
 | |
| 
 | |
|         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
 | |
|         while ((*G.endHPFS = *p++) != '\0')     /* copy to HPFS filename */
 | |
|             ++G.endHPFS;
 | |
|         if (IsFileNameValid(G.buildpathHPFS)) {
 | |
|             p = pathcomp;
 | |
|             while ((*G.endFAT = *p++) != '\0')  /* copy to FAT filename, too */
 | |
|                 ++G.endFAT;
 | |
|         } else
 | |
|             map2fat(pathcomp, &G.endFAT);   /* map into FAT fn, update endFAT */
 | |
| 
 | |
|         /* GRR:  could do better check, see if overrunning buffer as we go:
 | |
|          * check endHPFS-buildpathHPFS after each append, set warning variable
 | |
|          * if within 20 of FILNAMSIZ; then if var set, do careful check when
 | |
|          * appending.  Clear variable when begin new path. */
 | |
| 
 | |
|         /* next check:  need to append '/', at least one-char name, '\0' */
 | |
|         if ((G.endHPFS-G.buildpathHPFS) > FILNAMSIZ-3)
 | |
|             too_long = TRUE;                    /* check if extracting dir? */
 | |
| #ifdef FIX_STAT_BUG
 | |
|         /* Borland C++ 5.0 does not handle a call to stat() well if the
 | |
|          * directory does not exist (it tends to crash in strange places.)
 | |
|          * This is apparently a problem only when compiling for GUI rather
 | |
|          * than console. The code below attempts to work around this problem.
 | |
|          */
 | |
|         if (access(G.buildpathFAT, 0) != 0) {
 | |
|             if (!G.create_dirs) { /* told not to create (freshening) */
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 2;         /* path doesn't exist:  nothing to do */
 | |
|             }
 | |
|             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "checkdir error:  path too long: %s\n",
 | |
|                   FnFilter1(G.buildpathHPFS)));
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 4;         /* no room for filenames:  fatal */
 | |
|             }
 | |
|             if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "checkdir error:  can't create %s\n\
 | |
|                  unable to process %s.\n",
 | |
|                   FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 3;      /* path didn't exist, tried to create, failed */
 | |
|             }
 | |
|             G.created_dir = TRUE;
 | |
|         }
 | |
| #endif /* FIX_STAT_BUG */
 | |
|         if (SSTAT(G.buildpathFAT, &G.statbuf))   /* path doesn't exist */
 | |
|         {
 | |
|             if (!G.create_dirs) { /* told not to create (freshening) */
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 2;         /* path doesn't exist:  nothing to do */
 | |
|             }
 | |
|             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "checkdir error:  path too long: %s\n",
 | |
|                   FnFilter1(G.buildpathHPFS)));
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 4;         /* no room for filenames:  fatal */
 | |
|             }
 | |
|             if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "checkdir error:  can't create %s\n\
 | |
|                  unable to process %s.\n",
 | |
|                   FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return 3;      /* path didn't exist, tried to create, failed */
 | |
|             }
 | |
|             G.created_dir = TRUE;
 | |
|         } else if (!S_ISDIR(G.statbuf.st_mode)) {
 | |
|             Info(slide, 1, ((char *)slide,
 | |
|               "checkdir error:  %s exists but is not directory\n   \
 | |
|               unable to process %s.\n",
 | |
|               FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
 | |
|             free(G.buildpathHPFS);
 | |
|             free(G.buildpathFAT);
 | |
|             return 3;          /* path existed but wasn't dir */
 | |
|         }
 | |
|         if (too_long) {
 | |
|             Info(slide, 1, ((char *)slide,
 | |
|               "checkdir error:  path too long: %s\n",
 | |
|                FnFilter1(G.buildpathHPFS)));
 | |
|             free(G.buildpathHPFS);
 | |
|             free(G.buildpathFAT);
 | |
|             return 4;         /* no room for filenames:  fatal */
 | |
|         }
 | |
|         *G.endHPFS++ = '/';
 | |
|         *G.endFAT++ = '/';
 | |
|         *G.endHPFS = *G.endFAT = '\0';
 | |
|         Trace((stderr, "buildpathHPFS now = [%s]\nbuildpathFAT now =  [%s]\n",
 | |
|           FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
 | |
|         return 0;
 | |
| 
 | |
|     } /* end if (FUNCTION == APPEND_DIR) */
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     GETPATH:  copy full FAT path to the string pointed at by pathcomp (want
 | |
|     filename to reflect name used on disk, not EAs; if full path is HPFS,
 | |
|     buildpathFAT and buildpathHPFS will be identical).  Also free both paths.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (FUNCTION == GETPATH) {
 | |
|         Trace((stderr, "getting and freeing FAT path [%s]\n",
 | |
|           FnFilter1(G.buildpathFAT)));
 | |
|         Trace((stderr, "freeing HPFS path [%s]\n",
 | |
|           FnFilter1(G.buildpathHPFS)));
 | |
|         strcpy(pathcomp, G.buildpathFAT);
 | |
|         free(G.buildpathFAT);
 | |
|         free(G.buildpathHPFS);
 | |
|         G.buildpathHPFS = G.buildpathFAT = G.endHPFS = G.endFAT = NULL;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     APPEND_NAME:  assume the path component is the filename; append it and
 | |
|     return without checking for existence.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (FUNCTION == APPEND_NAME) {
 | |
|         char *p = pathcomp;
 | |
|         int error = 0;
 | |
| 
 | |
|         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
 | |
|         while ((*G.endHPFS = *p++) != '\0') {   /* copy to HPFS filename */
 | |
|             ++G.endHPFS;
 | |
|             if ((G.endHPFS-G.buildpathHPFS) >= FILNAMSIZ) {
 | |
|                 *--G.endHPFS = '\0';
 | |
|                 Info(slide, 1, ((char *)slide,
 | |
|                   "checkdir warning:  path too long; truncating\n \
 | |
|                   %s\n                -> %s\n",
 | |
|                   FnFilter1(G.filename), FnFilter2(G.buildpathHPFS)));
 | |
|                 error = 1;   /* filename truncated */
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ( G.pInfo->vollabel || IsFileNameValid(G.buildpathHPFS)) {
 | |
|             p = pathcomp;
 | |
|             while ((*G.endFAT = *p++) != '\0')  /* copy to FAT filename, too */
 | |
|                 ++G.endFAT;
 | |
|         } else
 | |
|             map2fat(pathcomp, &G.endFAT);   /* map into FAT fn, update endFAT */
 | |
|         Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT:  %s\n",
 | |
|           FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
 | |
| 
 | |
|         return error;  /* could check for existence, prompt for new name... */
 | |
| 
 | |
|     } /* end if (FUNCTION == APPEND_NAME) */
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     INIT:  allocate and initialize buffer space for the file currently being
 | |
|     extracted.  If file was renamed with an absolute path, don't prepend the
 | |
|     extract-to path.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (FUNCTION == INIT) {
 | |
|         Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
 | |
|         if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+1)) == NULL)
 | |
|             return 10;
 | |
|         if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+1)) == NULL) {
 | |
|             free(G.buildpathHPFS);
 | |
|             return 10;
 | |
|         }
 | |
|         if (G.pInfo->vollabel) { /* use root or renamed path, but don't store */
 | |
| /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
 | |
|             if (G.renamed_fullpath && pathcomp[1] == ':')
 | |
|                 *G.buildpathHPFS = (char)ToLower(*pathcomp);
 | |
|             else if (!G.renamed_fullpath && G.rootlen > 1 &&
 | |
|                      G.rootpath[1] == ':')
 | |
|                 *G.buildpathHPFS = (char)ToLower(*G.rootpath);
 | |
|             else {
 | |
|                 char tmpN[MAX_PATH], *tmpP;
 | |
|                 if (GetFullPathName(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
 | |
|                 { /* by definition of MAX_PATH we should never get here */
 | |
|                     Info(slide, 1, ((char *)slide,
 | |
|                       "checkdir warning: current dir path too long\n"));
 | |
|                     return 1;   /* can't get drive letter */
 | |
|                 }
 | |
|                 G.nLabelDrive = *tmpN - 'a' + 1;
 | |
|                 *G.buildpathHPFS = (char)(G.nLabelDrive - 1 + 'a');
 | |
|             }
 | |
|             G.nLabelDrive = *G.buildpathHPFS - 'a' + 1; /* save for mapname() */
 | |
|             if (G.volflag == 0 || *G.buildpathHPFS < 'a'  /* no labels/bogus? */
 | |
|                  || (G.volflag == 1 && !isfloppy(G.nLabelDrive))) { /* !fixed */
 | |
|                 free(G.buildpathHPFS);
 | |
|                 free(G.buildpathFAT);
 | |
|                 return IZ_VOL_LABEL;   /* skipping with message */
 | |
|             }
 | |
|             *G.buildpathHPFS = '\0';
 | |
|         } else if (G.renamed_fullpath) /* pathcomp = valid data */
 | |
|             strcpy(G.buildpathHPFS, pathcomp);
 | |
|         else if (G.rootlen > 0)
 | |
|             strcpy(G.buildpathHPFS, G.rootpath);
 | |
|         else
 | |
|             *G.buildpathHPFS = '\0';
 | |
|         G.endHPFS = G.buildpathHPFS;
 | |
|         G.endFAT = G.buildpathFAT;
 | |
|         while ((*G.endFAT = *G.endHPFS) != '\0') {
 | |
|             ++G.endFAT;
 | |
|             ++G.endHPFS;
 | |
|         }
 | |
|         Trace((stderr, "[%s]\n", FnFilter1(G.buildpathHPFS)));
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     ROOT:  if appropriate, store the path in rootpath and create it if neces-
 | |
|     sary; else assume it's a zipfile member and return.  This path segment
 | |
|     gets used in extracting all members from every zipfile specified on the
 | |
|     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
 | |
|     directory specification includes a drive letter (leading "x:"), it is
 | |
|     treated just as if it had a trailing '/'--that is, one directory level
 | |
|     will be created if the path doesn't exist, unless this is otherwise pro-
 | |
|     hibited (e.g., freshening).
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
| #if (!defined(SFX) || defined(SFX_EXDIR))
 | |
|     if (FUNCTION == ROOT) {
 | |
|         Trace((stderr, "initializing root path to [%s]\n",
 | |
|           FnFilter1(pathcomp)));
 | |
|         if (pathcomp == NULL) {
 | |
|             G.rootlen = 0;
 | |
|             return 0;
 | |
|         }
 | |
|         if ((G.rootlen = strlen(pathcomp)) > 0) {
 | |
|             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
 | |
| 
 | |
|             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
 | |
|                 has_drive = TRUE;   /* drive designator */
 | |
|             if (pathcomp[G.rootlen-1] == '/' || pathcomp[G.rootlen-1] == '\\') {
 | |
|                 pathcomp[--G.rootlen] = '\0';
 | |
|                 had_trailing_pathsep = TRUE;
 | |
|             }
 | |
|             if (has_drive && (G.rootlen == 2)) {
 | |
|                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
 | |
|                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
 | |
|             } else if (G.rootlen > 0) {   /* need not check "x:." and "x:/" */
 | |
|                 if (SSTAT(pathcomp, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
 | |
|                 {
 | |
|                     /* path does not exist */
 | |
|                     if (!G.create_dirs /* || iswild(pathcomp) */ ) {
 | |
|                         G.rootlen = 0;
 | |
|                         return 2;   /* treat as stored file */
 | |
|                     }
 | |
|                     /* create directory (could add loop here to scan pathcomp
 | |
|                      * and create more than one level, but really necessary?) */
 | |
|                     if (MKDIR(pathcomp, 0777) == -1) {
 | |
|                         Info(slide, 1, ((char *)slide,
 | |
|                           "checkdir:  can't create extraction directory: %s\n",
 | |
|                           FnFilter1(pathcomp)));
 | |
|                         G.rootlen = 0; /* path didn't exist, tried to create, */
 | |
|                         return 3;  /* failed:  file exists, or need 2+ levels */
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if ((G.rootpath = (char *)malloc(G.rootlen+xtra)) == NULL) {
 | |
|                 G.rootlen = 0;
 | |
|                 return 10;
 | |
|             }
 | |
|             strcpy(G.rootpath, pathcomp);
 | |
|             if (xtra == 3)                  /* had just "x:", make "x:." */
 | |
|                 G.rootpath[G.rootlen++] = '.';
 | |
|             G.rootpath[G.rootlen++] = '/';
 | |
|             G.rootpath[G.rootlen] = '\0';
 | |
|             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
| #endif /* !SFX || SFX_EXDIR */
 | |
| 
 | |
| /*---------------------------------------------------------------------------
 | |
|     END:  free rootpath, immediately prior to program exit.
 | |
|   ---------------------------------------------------------------------------*/
 | |
| 
 | |
|     if (FUNCTION == END) {
 | |
|         Trace((stderr, "freeing rootpath\n"));
 | |
|         if (G.rootlen > 0)
 | |
|             free(G.rootpath);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     return 99;  /* should never reach */
 | |
| 
 | |
| } /* end function checkdir() */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifndef SFX
 | |
| #ifndef WINDLL
 | |
| 
 | |
| /************************/
 | |
| /*  Function version()  */
 | |
| /************************/
 | |
| 
 | |
| void version(__G)
 | |
|     __GDEF
 | |
| {
 | |
|     int len;
 | |
| #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
 | |
|     char buf[80];
 | |
| #if (defined(_MSC_VER) && (_MSC_VER > 900))
 | |
|     char buf2[80];
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
|     len = sprintf((char *)slide, CompiledWith,
 | |
| 
 | |
| #ifdef _MSC_VER  /* MSC == VC++, but what about SDK compiler? */
 | |
|       (sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100), buf),
 | |
| #  if (_MSC_VER == 800)
 | |
|       "(Visual C++ v1.1)",
 | |
| #  elif (_MSC_VER == 850)
 | |
|       "(Windows NT v3.5 SDK)",
 | |
| #  elif (_MSC_VER == 900)
 | |
|       "(Visual C++ v2.x)",
 | |
| #  elif (_MSC_VER > 900)
 | |
|       (sprintf(buf2, "(Visual C++ %d.%d)", _MSC_VER/100 - 6, _MSC_VER%100/10),
 | |
|         buf2),
 | |
| #  else
 | |
|       "(bad version)",
 | |
| #  endif
 | |
| #elif defined(__WATCOMC__)
 | |
| #  if (__WATCOMC__ % 10 > 0)
 | |
|       (sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
 | |
|        __WATCOMC__ % 100), buf), "",
 | |
| #  else
 | |
|       (sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
 | |
|        (__WATCOMC__ % 100) / 10), buf), "",
 | |
| #  endif
 | |
| #elif defined(__BORLANDC__)
 | |
|       "Borland C++",
 | |
| #  if (__BORLANDC__ < 0x0200)
 | |
|       " 1.0",
 | |
| #  elif (__BORLANDC__ == 0x0200)
 | |
|       " 2.0",
 | |
| #  elif (__BORLANDC__ == 0x0400)
 | |
|       " 3.0",
 | |
| #  elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
 | |
|       " 3.1",
 | |
| #  elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
 | |
|       " 4.0 or 4.02",
 | |
| #  elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
 | |
|       " 4.5",
 | |
| #  elif (__BORLANDC__ == 0x0500)   /* GRR:  assume this will stay sync'd? */
 | |
|       " 5.0",
 | |
| #  else
 | |
|       " later than 5.0",
 | |
| #  endif
 | |
| #elif defined(__GNUC__)
 | |
| #  ifdef __RSXNT__
 | |
| #    if defined(__EMX__)
 | |
|       "rsxnt(emx)+gcc ",
 | |
| #    elif defined(__DJGPP__)
 | |
|       (sprintf(buf, "rsxnt(djgpp) v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__),
 | |
|        buf),
 | |
| #    elif defined(__GO32__)
 | |
|       "rsxnt(djgpp) v1.x / gcc ",
 | |
| #    else
 | |
|       "rsxnt(unknown) / gcc ",
 | |
| #    endif
 | |
| #  else
 | |
|       "gcc ",
 | |
| #  endif
 | |
|       __VERSION__,
 | |
| #else /* !_MSC_VER, !__WATCOMC__, !__BORLANDC__, !__GNUC__ */
 | |
|       "unknown compiler (SDK?)", "",
 | |
| #endif /* ?compilers */
 | |
| 
 | |
|       "Windows 95 / Windows NT", "\n(32-bit)",
 | |
| 
 | |
| #ifdef __DATE__
 | |
|       " on ", __DATE__
 | |
| #else
 | |
|       "", ""
 | |
| #endif
 | |
|     );
 | |
| 
 | |
|     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
 | |
| 
 | |
|     return;
 | |
| 
 | |
| } /* end function version() */
 | |
| 
 | |
| #endif /* !WINDLL */
 | |
| #endif /* !SFX */
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifdef __WATCOMC__
 | |
| 
 | |
| /* This papers over a bug in Watcom 10.6's standard library... sigh */
 | |
| /* Apparently it applies to both the DOS and Win32 stat()s.         */
 | |
| /* I believe they said this was fixed for 11.0?                     */
 | |
| 
 | |
| int stat_bandaid(const char *path, struct stat *buf)
 | |
| {
 | |
|   char newname[4];
 | |
|   if (!stat(path, buf))
 | |
|     return 0;
 | |
|   else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
 | |
|     strcpy(newname, path);
 | |
|     newname[strlen(path) - 1] = '\\';   /* stat(".") fails for root! */
 | |
|     return stat(newname, buf);
 | |
|   } else
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| /* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
 | |
| /* Note that if PASSWD_FROM_STDIN is defined, the file containing   */
 | |
| /* the password must have a carriage return after the word, not a   */
 | |
| /* Unix-style newline (linefeed only).  This discards linefeeds.    */
 | |
| 
 | |
| int getch(void)
 | |
| {
 | |
|   HANDLE stin;
 | |
|   DWORD rc;
 | |
|   unsigned char buf[2];
 | |
|   int ret = -1;
 | |
| 
 | |
| #  ifdef PASSWD_FROM_STDIN
 | |
|   DWORD odemode = ~0;
 | |
|   stin = GetStdHandle(STD_INPUT_HANDLE);
 | |
|   if (GetConsoleMode(stin, &odemode))
 | |
|     SetConsoleMode(stin, ENABLE_PROCESSED_INPUT);  /* raw except ^C noticed */
 | |
| #  else
 | |
|   if (!(stin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
 | |
|                           FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)))
 | |
|     return -1;
 | |
|   SetConsoleMode(stin, ENABLE_PROCESSED_INPUT);    /* raw except ^C noticed */
 | |
| #  endif
 | |
|   if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
 | |
|     ret = buf[0];
 | |
|   /* when the user hits return we get CR LF.  We discard the LF, not the CR,
 | |
|    * because when we call this for the first time after a previous input
 | |
|    * such as the one for "replace foo? [y]es, ..." the LF may still be in
 | |
|    * the input stream before whatever the user types at our prompt. */
 | |
|   if (ret == '\n')
 | |
|     if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
 | |
|       ret = buf[0];
 | |
| #  ifdef PASSWD_FROM_STDIN
 | |
|   if (odemode != ~0)
 | |
|     SetConsoleMode(stin, odemode);
 | |
| #  else
 | |
|   CloseHandle(stin);
 | |
| #  endif
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| #endif /* __WATCOMC__ */
 |