~ubuntu-branches/ubuntu/precise/unzip/precise-proposed

« back to all changes in this revision

Viewing changes to msdos/msdos.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2004-06-06 17:57:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040606175746-nl7p2dgp3aobyc2c
Tags: upstream-5.51
ImportĀ upstreamĀ versionĀ 5.51

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (c) 1990-2003 Info-ZIP.  All rights reserved.
 
3
 
 
4
  See the accompanying file LICENSE, version 2000-Apr-09 or later
 
5
  (the contents of which are also included in unzip.h) for terms of use.
 
6
  If, for some reason, all these files are missing, the Info-ZIP license
 
7
  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
 
8
*/
 
9
/*---------------------------------------------------------------------------
 
10
 
 
11
  msdos.c
 
12
 
 
13
  MSDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later.
 
14
 
 
15
  Contains:  Opendir()                      (from zip)
 
16
             Readdir()                      (from zip)
 
17
             do_wild()
 
18
             mapattr()
 
19
             mapname()
 
20
             maskDOSdevice()
 
21
             map2fat()
 
22
             checkdir()
 
23
             isfloppy()
 
24
             z_dos_chmod()
 
25
             volumelabel()                  (non-djgpp, non-emx)
 
26
             close_outfile()
 
27
             stamp_file()                   (TIMESTAMP only)
 
28
             dateformat()
 
29
             version()
 
30
             zcalloc()                      (16-bit, only)
 
31
             zcfree()                       (16-bit, only)
 
32
             _dos_getcountryinfo()          (djgpp 1.x, emx)
 
33
            [_dos_getftime()                (djgpp 1.x, emx)   to be added]
 
34
             _dos_setftime()                (djgpp 1.x, emx)
 
35
             _dos_setfileattr()             (djgpp 1.x, emx)
 
36
             _dos_getdrive()                (djgpp 1.x, emx)
 
37
             _dos_creat()                   (djgpp 1.x, emx)
 
38
             _dos_close()                   (djgpp 1.x, emx)
 
39
             volumelabel()                  (djgpp, emx)
 
40
             _dos_getcountryinfo()          (djgpp 2.x)
 
41
             _is_executable()               (djgpp 2.x)
 
42
             __crt0_glob_function()         (djgpp 2.x)
 
43
             __crt0_load_environment_file() (djgpp 2.x)
 
44
             screensize()                   (emx, Watcom 32-bit)
 
45
             int86x_realmode()              (Watcom 32-bit)
 
46
             stat_bandaid()                 (Watcom)
 
47
 
 
48
  ---------------------------------------------------------------------------*/
 
49
 
 
50
 
 
51
 
 
52
#define UNZIP_INTERNAL
 
53
#include "unzip.h"
 
54
 
 
55
/* fUnZip does not need anything from here except the zcalloc() & zcfree()
 
56
 * function pair (when Deflate64 support is enabled in 16-bit environment).
 
57
 */
 
58
#ifndef FUNZIP
 
59
 
 
60
static void maskDOSdevice(__GPRO__ char *pathcomp, char *last_dot);
 
61
#ifdef MAYBE_PLAIN_FAT
 
62
   static void map2fat OF((char *pathcomp, char *last_dot));
 
63
#endif
 
64
static int isfloppy OF((int nDrive));
 
65
static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
 
66
static int volumelabel OF((ZCONST char *newlabel));
 
67
 
 
68
static int created_dir;        /* used by mapname(), checkdir() */
 
69
static int renamed_fullpath;   /* ditto */
 
70
static unsigned nLabelDrive;   /* ditto, plus volumelabel() */
 
71
 
 
72
 
 
73
 
 
74
/*****************************/
 
75
/*  Strings used in msdos.c  */
 
76
/*****************************/
 
77
 
 
78
#ifndef SFX
 
79
  static ZCONST char Far CantAllocateWildcard[] =
 
80
    "warning:  cannot allocate wildcard buffers\n";
 
81
#endif
 
82
static ZCONST char Far WarnDirTraversSkip[] =
 
83
  "warning:  skipped \"../\" path component(s) in %s\n";
 
84
static ZCONST char Far Creating[] = "   creating: %s\n";
 
85
static ZCONST char Far ConversionFailed[] =
 
86
  "mapname:  conversion of %s failed\n";
 
87
static ZCONST char Far Labelling[] = "labelling %c: %-22s\n";
 
88
static ZCONST char Far ErrSetVolLabel[] =
 
89
  "mapname:  error setting volume label\n";
 
90
static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
 
91
static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %s\n\
 
92
                 unable to process %s.\n";
 
93
static ZCONST char Far DirIsntDirectory[] =
 
94
  "checkdir error:  %s exists but is not directory\n\
 
95
                 unable to process %s.\n";
 
96
static ZCONST char Far PathTooLongTrunc[] =
 
97
  "checkdir warning:  path too long; truncating\n                   %s\n\
 
98
                -> %s\n";
 
99
#if (!defined(SFX) || defined(SFX_EXDIR))
 
100
   static ZCONST char Far CantCreateExtractDir[] =
 
101
     "checkdir:  cannot create extraction directory: %s\n";
 
102
#endif
 
103
static ZCONST char Far AttribsMayBeWrong[] =
 
104
  "\nwarning:  file attributes may not be correct\n";
 
105
 
 
106
 
 
107
 
 
108
/****************************/
 
109
/*  Macros used in msdos.c  */
 
110
/****************************/
 
111
 
 
112
#ifdef WATCOMC_386
 
113
#  define WREGS(v,r) (v.w.r)
 
114
#  define int86x int386x
 
115
   static int int86x_realmode(int inter_no, union REGS *in,
 
116
                              union REGS *out, struct SREGS *seg);
 
117
#  define F_intdosx(ir,or,sr) int86x_realmode(0x21, ir, or, sr)
 
118
#  define XXX__MK_FP_IS_BROKEN
 
119
#else
 
120
#  define WREGS(v,r) (v.x.r)
 
121
#  define F_intdosx(ir,or,sr) intdosx(ir, or, sr)
 
122
#endif
 
123
 
 
124
#if (defined(__GO32__) || defined(__EMX__))
 
125
#  include <dirent.h>        /* use readdir() */
 
126
#  define MKDIR(path,mode)   mkdir(path,mode)
 
127
#  define Opendir  opendir
 
128
#  define Readdir  readdir
 
129
#  define Closedir closedir
 
130
#  define zdirent  dirent
 
131
#  define zDIR     DIR
 
132
#  ifdef __EMX__
 
133
#    include <dos.h>
 
134
#    define GETDRIVE(d)      d = _getdrive()
 
135
#    define FA_LABEL         A_LABEL
 
136
#  else
 
137
#    define GETDRIVE(d)      _dos_getdrive(&d)
 
138
#  endif
 
139
#  if defined(_A_SUBDIR)     /* MSC dos.h and compatibles */
 
140
#    define FSUBDIR          _A_SUBDIR
 
141
#  elif defined(FA_DIREC)    /* Borland dos.h and compatible variants */
 
142
#    define FSUBDIR          FA_DIREC
 
143
#  elif defined(A_DIR)       /* EMX dir.h (and dirent.h) */
 
144
#    define FSUBDIR          A_DIR
 
145
#  else                      /* fallback definition */
 
146
#    define FSUBDIR          0x10
 
147
#  endif
 
148
#  if defined(_A_VOLID)      /* MSC dos.h and compatibles */
 
149
#    define FVOLID           _A_VOLID
 
150
#  elif defined(FA_LABEL)    /* Borland dos.h and compatible variants */
 
151
#    define FVOLID           FA_LABEL
 
152
#  elif defined(A_LABEL)     /* EMX dir.h (and dirent.h) */
 
153
#    define FVOLID           A_LABEL
 
154
#  else
 
155
#    define FVOLID           0x08
 
156
#  endif
 
157
#else /* !(__GO32__ || __EMX__) */
 
158
#  define MKDIR(path,mode)   mkdir(path)
 
159
#  ifdef __TURBOC__
 
160
#    define FATTR            FA_HIDDEN+FA_SYSTEM+FA_DIREC
 
161
#    define FVOLID           FA_LABEL
 
162
#    define FSUBDIR          FA_DIREC
 
163
#    define FFIRST(n,d,a)    findfirst(n,(struct ffblk *)d,a)
 
164
#    define FNEXT(d)         findnext((struct ffblk *)d)
 
165
#    define GETDRIVE(d)      d=getdisk()+1
 
166
#    include <dir.h>
 
167
#  else /* !__TURBOC__ */
 
168
#    define FATTR            _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
 
169
#    define FVOLID           _A_VOLID
 
170
#    define FSUBDIR          _A_SUBDIR
 
171
#    define FFIRST(n,d,a)    _dos_findfirst(n,a,(struct find_t *)d)
 
172
#    define FNEXT(d)         _dos_findnext((struct find_t *)d)
 
173
#    define GETDRIVE(d)      _dos_getdrive(&d)
 
174
#    include <direct.h>
 
175
#  endif /* ?__TURBOC__ */
 
176
   typedef struct zdirent {
 
177
       char d_reserved[30];
 
178
       char d_name[13];
 
179
       int d_first;
 
180
   } zDIR;
 
181
   zDIR *Opendir OF((const char *));
 
182
   struct zdirent *Readdir OF((zDIR *));
 
183
#  define Closedir free
 
184
 
 
185
 
 
186
 
 
187
 
 
188
#ifndef SFX
 
189
 
 
190
/**********************/   /* Borland C++ 3.x has its own opendir/readdir */
 
191
/* Function Opendir() */   /*  library routines, but earlier versions don't, */
 
192
/**********************/   /*  so use ours regardless */
 
193
 
 
194
zDIR *Opendir(name)
 
195
    const char *name;           /* name of directory to open */
 
196
{
 
197
    zDIR *dirp;                 /* malloc'd return value */
 
198
    char *nbuf;                 /* malloc'd temporary string */
 
199
    extent len = strlen(name);  /* path length to avoid strlens and strcats */
 
200
 
 
201
 
 
202
    if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL)
 
203
        return (zDIR *)NULL;
 
204
    if ((nbuf = malloc(len + 6)) == (char *)NULL) {
 
205
        free(dirp);
 
206
        return (zDIR *)NULL;
 
207
    }
 
208
    strcpy(nbuf, name);
 
209
    if (len > 0) {
 
210
        if (nbuf[len-1] == ':') {
 
211
            nbuf[len++] = '.';
 
212
        } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\')
 
213
            --len;
 
214
    }
 
215
    strcpy(nbuf+len, "/*.*");
 
216
    Trace((stderr, "Opendir:  nbuf = [%s]\n", FnFilter1(nbuf)));
 
217
 
 
218
    if (FFIRST(nbuf, dirp, FATTR)) {
 
219
        free((zvoid *)nbuf);
 
220
        return (zDIR *)NULL;
 
221
    }
 
222
    free((zvoid *)nbuf);
 
223
    dirp->d_first = 1;
 
224
    return dirp;
 
225
}
 
226
 
 
227
 
 
228
 
 
229
 
 
230
 
 
231
/**********************/
 
232
/* Function Readdir() */
 
233
/**********************/
 
234
 
 
235
struct zdirent *Readdir(d)
 
236
    zDIR *d;        /* directory stream from which to read */
 
237
{
 
238
    /* Return pointer to first or next directory entry, or NULL if end. */
 
239
 
 
240
    if (d->d_first)
 
241
        d->d_first = 0;
 
242
    else
 
243
        if (FNEXT(d))
 
244
            return (struct zdirent *)NULL;
 
245
    return (struct zdirent *)d;
 
246
}
 
247
 
 
248
#endif /* !SFX */
 
249
#endif /* ?(__GO32__ || __EMX__) */
 
250
 
 
251
 
 
252
 
 
253
 
 
254
 
 
255
#ifndef SFX
 
256
 
 
257
/************************/
 
258
/*  Function do_wild()  */   /* identical to OS/2 version */
 
259
/************************/
 
260
 
 
261
char *do_wild(__G__ wildspec)
 
262
    __GDEF
 
263
    ZCONST char *wildspec;   /* only used first time on a given dir */
 
264
{
 
265
    static zDIR *wild_dir = (zDIR *)NULL;
 
266
    static ZCONST char *wildname;
 
267
    static char *dirname, matchname[FILNAMSIZ];
 
268
    static int notfirstcall=FALSE, have_dirname, dirnamelen;
 
269
    char *fnamestart;
 
270
    struct zdirent *file;
 
271
 
 
272
    /* Even when we're just returning wildspec, we *always* do so in
 
273
     * matchname[]--calling routine is allowed to append four characters
 
274
     * to the returned string, and wildspec may be a pointer to argv[].
 
275
     */
 
276
    if (!notfirstcall) {    /* first call:  must initialize everything */
 
277
        notfirstcall = TRUE;
 
278
 
 
279
        if (!iswild(wildspec)) {
 
280
            strcpy(matchname, wildspec);
 
281
            have_dirname = FALSE;
 
282
            wild_dir = NULL;
 
283
            return matchname;
 
284
        }
 
285
 
 
286
        /* break the wildspec into a directory part and a wildcard filename */
 
287
        if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL &&
 
288
            (wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) {
 
289
            dirname = ".";
 
290
            dirnamelen = 1;
 
291
            have_dirname = FALSE;
 
292
            wildname = wildspec;
 
293
        } else {
 
294
            ++wildname;     /* point at character after '/' or ':' */
 
295
            dirnamelen = (int)(wildname - wildspec);
 
296
            if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
 
297
                Info(slide, 1, ((char *)slide,
 
298
                  LoadFarString(CantAllocateWildcard)));
 
299
                strcpy(matchname, wildspec);
 
300
                return matchname;   /* but maybe filespec was not a wildcard */
 
301
            }
 
302
/* GRR:  can't strip trailing char for opendir since might be "d:/" or "d:"
 
303
 *       (would have to check for "./" at end--let opendir handle it instead) */
 
304
            strncpy(dirname, wildspec, dirnamelen);
 
305
            dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
 
306
            have_dirname = TRUE;
 
307
        }
 
308
        Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(dirname)));
 
309
 
 
310
        if ((wild_dir = Opendir(dirname)) != (zDIR *)NULL) {
 
311
            if (have_dirname) {
 
312
                strcpy(matchname, dirname);
 
313
                fnamestart = matchname + dirnamelen;
 
314
            } else
 
315
                fnamestart = matchname;
 
316
            while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
 
317
                Trace((stderr, "do_wild:  readdir returns %s\n",
 
318
                  FnFilter1(file->d_name)));
 
319
                strcpy(fnamestart, file->d_name);
 
320
                if (strrchr(fnamestart, '.') == (char *)NULL)
 
321
                    strcat(fnamestart, ".");
 
322
                if (match(fnamestart, wildname, 1) &&  /* 1 == ignore case */
 
323
                    /* skip "." and ".." directory entries */
 
324
                    strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
 
325
                    Trace((stderr, "do_wild:  match() succeeds\n"));
 
326
                    /* remove trailing dot */
 
327
                    fnamestart += strlen(fnamestart) - 1;
 
328
                    if (*fnamestart == '.')
 
329
                        *fnamestart = '\0';
 
330
                    return matchname;
 
331
                }
 
332
            }
 
333
            /* if we get to here directory is exhausted, so close it */
 
334
            Closedir(wild_dir);
 
335
            wild_dir = (zDIR *)NULL;
 
336
        }
 
337
#ifdef DEBUG
 
338
        else {
 
339
            Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n",
 
340
               FnFilter1(dirname)));
 
341
        }
 
342
#endif /* DEBUG */
 
343
 
 
344
        /* return the raw wildspec in case that works (e.g., directory not
 
345
         * searchable, but filespec was not wild and file is readable) */
 
346
        strcpy(matchname, wildspec);
 
347
        return matchname;
 
348
    }
 
349
 
 
350
    /* last time through, might have failed opendir but returned raw wildspec */
 
351
    if (wild_dir == (zDIR *)NULL) {
 
352
        notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
 
353
        if (have_dirname)
 
354
            free(dirname);
 
355
        return (char *)NULL;
 
356
    }
 
357
 
 
358
    /* If we've gotten this far, we've read and matched at least one entry
 
359
     * successfully (in a previous call), so dirname has been copied into
 
360
     * matchname already.
 
361
     */
 
362
    if (have_dirname) {
 
363
        /* strcpy(matchname, dirname); */
 
364
        fnamestart = matchname + dirnamelen;
 
365
    } else
 
366
        fnamestart = matchname;
 
367
    while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
 
368
        Trace((stderr, "do_wild:  readdir returns %s\n",
 
369
          FnFilter1(file->d_name)));
 
370
        strcpy(fnamestart, file->d_name);
 
371
        if (strrchr(fnamestart, '.') == (char *)NULL)
 
372
            strcat(fnamestart, ".");
 
373
        if (match(fnamestart, wildname, 1)) {   /* 1 == ignore case */
 
374
            Trace((stderr, "do_wild:  match() succeeds\n"));
 
375
            /* remove trailing dot */
 
376
            fnamestart += strlen(fnamestart) - 1;
 
377
            if (*fnamestart == '.')
 
378
                *fnamestart = '\0';
 
379
            return matchname;
 
380
        }
 
381
    }
 
382
 
 
383
    Closedir(wild_dir);     /* have read at least one entry; nothing left */
 
384
    wild_dir = (zDIR *)NULL;
 
385
    notfirstcall = FALSE;   /* reset for new wildspec */
 
386
    if (have_dirname)
 
387
        free(dirname);
 
388
    return (char *)NULL;
 
389
 
 
390
} /* end function do_wild() */
 
391
 
 
392
#endif /* !SFX */
 
393
 
 
394
 
 
395
 
 
396
 
 
397
/**********************/
 
398
/* Function mapattr() */
 
399
/**********************/
 
400
 
 
401
int mapattr(__G)
 
402
    __GDEF
 
403
{
 
404
    /* set archive bit for file entries (file is not backed up): */
 
405
    G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
 
406
      (G.crec.external_file_attributes & FSUBDIR ? 0 : 32)) & 0xff;
 
407
    return 0;
 
408
 
 
409
} /* end function mapattr() */
 
410
 
 
411
 
 
412
 
 
413
 
 
414
 
 
415
/************************/
 
416
/*  Function mapname()  */
 
417
/************************/
 
418
 
 
419
int mapname(__G__ renamed)
 
420
    __GDEF
 
421
    int renamed;
 
422
/*
 
423
 * returns:
 
424
 *  MPN_OK          - no problem detected
 
425
 *  MPN_INF_TRUNC   - caution (truncated filename)
 
426
 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
 
427
 *  MPN_ERR_SKIP    - error -> skip entry
 
428
 *  MPN_ERR_TOOLONG - error -> path is too long
 
429
 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
 
430
 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
 
431
 */
 
432
{
 
433
    char pathcomp[FILNAMSIZ];      /* path-component buffer */
 
434
    char *pp, *cp=(char *)NULL;    /* character pointers */
 
435
    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
 
436
#ifdef MAYBE_PLAIN_FAT
 
437
    char *last_dot=(char *)NULL;   /* last dot not converted to underscore */
 
438
# ifdef USE_LFN
 
439
    int use_lfn = USE_LFN;         /* file system supports long filenames? */
 
440
# endif
 
441
#endif
 
442
    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
 
443
    int error = MPN_OK;
 
444
    register unsigned workch;      /* hold the character being tested */
 
445
 
 
446
 
 
447
/*---------------------------------------------------------------------------
 
448
    Initialize various pointers and counters and stuff.
 
449
  ---------------------------------------------------------------------------*/
 
450
 
 
451
    /* can create path as long as not just freshening, or if user told us */
 
452
    G.create_dirs = (!uO.fflag || renamed);
 
453
 
 
454
    created_dir = FALSE;        /* not yet */
 
455
    renamed_fullpath = FALSE;
 
456
 
 
457
    if (renamed) {
 
458
        cp = G.filename - 1;    /* point to beginning of renamed name... */
 
459
        while (*++cp)
 
460
            if (*cp == '\\')    /* convert backslashes to forward */
 
461
                *cp = '/';
 
462
        cp = G.filename;
 
463
        /* use temporary rootpath if user gave full pathname */
 
464
        if (G.filename[0] == '/') {
 
465
            renamed_fullpath = TRUE;
 
466
            pathcomp[0] = '/';  /* copy the '/' and terminate */
 
467
            pathcomp[1] = '\0';
 
468
            ++cp;
 
469
        } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
 
470
            renamed_fullpath = TRUE;
 
471
            pp = pathcomp;
 
472
            *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
 
473
            *pp++ = *cp++;
 
474
            if (*cp == '/')
 
475
                *pp++ = *cp++;  /* otherwise add "./"? */
 
476
            *pp = '\0';
 
477
        }
 
478
    }
 
479
 
 
480
    /* pathcomp is ignored unless renamed_fullpath is TRUE: */
 
481
    if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */
 
482
        return error;           /* ...unless no mem or vol label on hard disk */
 
483
 
 
484
    *pathcomp = '\0';           /* initialize translation buffer */
 
485
    pp = pathcomp;              /* point to translation buffer */
 
486
    if (!renamed) {             /* cp already set if renamed */
 
487
        if (uO.jflag)           /* junking directories */
 
488
            cp = (char *)strrchr(G.filename, '/');
 
489
        if (cp == (char *)NULL) /* no '/' or not junking dirs */
 
490
            cp = G.filename;    /* point to internal zipfile-member pathname */
 
491
        else
 
492
            ++cp;               /* point to start of last component of path */
 
493
    }
 
494
 
 
495
/*---------------------------------------------------------------------------
 
496
    Begin main loop through characters in filename.
 
497
  ---------------------------------------------------------------------------*/
 
498
 
 
499
    while ((workch = (uch)*cp++) != 0) {
 
500
 
 
501
        switch (workch) {
 
502
            case '/':             /* can assume -j flag not given */
 
503
                *pp = '\0';
 
504
#ifdef MAYBE_PLAIN_FAT
 
505
                maskDOSdevice(__G__ pathcomp, last_dot);
 
506
#else
 
507
                maskDOSdevice(__G__ pathcomp, NULL);
 
508
#endif
 
509
#ifdef MAYBE_PLAIN_FAT
 
510
# ifdef USE_LFN
 
511
                if (!use_lfn)
 
512
# endif
 
513
                {
 
514
                    map2fat(pathcomp, last_dot);   /* 8.3 trunc. (in place) */
 
515
                    last_dot = (char *)NULL;
 
516
                }
 
517
#endif
 
518
                if (strcmp(pathcomp, ".") == 0) {
 
519
                    /* don't bother appending "./" to the path */
 
520
                    *pathcomp = '\0';
 
521
                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
 
522
                    /* "../" dir traversal detected, skip over it */
 
523
                    *pathcomp = '\0';
 
524
                    killed_ddot = TRUE;     /* set "show message" flag */
 
525
                }
 
526
                /* when path component is not empty, append it now */
 
527
                if (*pathcomp != '\0' &&
 
528
                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
 
529
                     & MPN_MASK) > MPN_INF_TRUNC)
 
530
                    return error;
 
531
                pp = pathcomp;    /* reset conversion buffer for next piece */
 
532
                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
 
533
                break;
 
534
 
 
535
#ifdef MAYBE_PLAIN_FAT
 
536
            case '.':
 
537
# ifdef USE_LFN
 
538
                if (use_lfn) {          /* LFN filenames may contain many */
 
539
                    *pp++ = '.';        /*  dots, so simply copy it ... */
 
540
                } else
 
541
# endif
 
542
                if (pp == pathcomp && *cp == '.' && cp[1] == '/') {
 
543
                    /* nothing appended yet.., and found "../" */
 
544
                    *pp++ = '.';        /*  add first dot, */
 
545
                    *pp++ = '.';        /*  second dot, and */
 
546
                    ++cp;               /*  skip over to the '/' */
 
547
                } else {                /* found dot within path component */
 
548
                    last_dot = pp;      /*  point at last dot so far... */
 
549
                    *pp++ = '_';        /*  convert to underscore for now */
 
550
                }
 
551
                break;
 
552
#endif /* MAYBE_PLAIN_FAT */
 
553
 
 
554
            /* drive names are not stored in zipfile, so no colons allowed;
 
555
             *  no brackets or most other punctuation either (all of which
 
556
             *  can appear in Unix-created archives; backslash is particularly
 
557
             *  bad unless all necessary directories exist) */
 
558
#ifdef MAYBE_PLAIN_FAT
 
559
            case '[':          /* these punctuation characters forbidden */
 
560
            case ']':          /*  only on plain FAT file systems */
 
561
            case '+':
 
562
            case ',':
 
563
            case '=':
 
564
# ifdef USE_LFN
 
565
                if (use_lfn)
 
566
                    *pp++ = (char)workch;
 
567
                else
 
568
                    *pp++ = '_';
 
569
                break;
 
570
# endif
 
571
#endif
 
572
            case ':':           /* special shell characters of command.com */
 
573
            case '\\':          /*  (device and directory limiters, wildcard */
 
574
            case '"':           /*  characters, stdin/stdout redirection and */
 
575
            case '<':           /*  pipe indicators and the quote sign) are */
 
576
            case '>':           /*  never allowed in filenames on (V)FAT */
 
577
            case '|':
 
578
            case '*':
 
579
            case '?':
 
580
                *pp++ = '_';
 
581
                break;
 
582
 
 
583
            case ';':             /* start of VMS version? */
 
584
                lastsemi = pp;
 
585
#ifdef MAYBE_PLAIN_FAT
 
586
# ifdef USE_LFN
 
587
                if (use_lfn)
 
588
                    *pp++ = ';';  /* keep for now; remove VMS ";##" later */
 
589
# endif
 
590
#else
 
591
                *pp++ = ';';      /* keep for now; remove VMS ";##" later */
 
592
#endif
 
593
                break;
 
594
 
 
595
#ifdef MAYBE_PLAIN_FAT
 
596
            case ' ':                      /* change spaces to underscores */
 
597
# ifdef USE_LFN
 
598
                if (!use_lfn && uO.sflag)  /*  only if requested and NO lfn! */
 
599
# else
 
600
                if (uO.sflag)              /*  only if requested */
 
601
# endif
 
602
                    *pp++ = '_';
 
603
                else
 
604
                    *pp++ = (char)workch;
 
605
                break;
 
606
#endif /* MAYBE_PLAIN_FAT */
 
607
 
 
608
            default:
 
609
                /* allow ASCII 255 and European characters in filenames: */
 
610
                if (isprint(workch) || workch >= 127)
 
611
                    *pp++ = (char)workch;
 
612
 
 
613
        } /* end switch */
 
614
    } /* end while loop */
 
615
 
 
616
    /* Show warning when stripping insecure "parent dir" path components */
 
617
    if (killed_ddot && QCOND2) {
 
618
        Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip),
 
619
          FnFilter1(G.filename)));
 
620
        if (!(error & ~MPN_MASK))
 
621
            error = (error & MPN_MASK) | PK_WARN;
 
622
    }
 
623
 
 
624
/*---------------------------------------------------------------------------
 
625
    Report if directory was created (and no file to create:  filename ended
 
626
    in '/'), check name to be sure it exists, and combine path and name be-
 
627
    fore exiting.
 
628
  ---------------------------------------------------------------------------*/
 
629
 
 
630
    if (G.filename[strlen(G.filename) - 1] == '/') {
 
631
        checkdir(__G__ G.filename, GETPATH);
 
632
        if (created_dir) {
 
633
            if (QCOND2) {
 
634
                Info(slide, 0, ((char *)slide, LoadFarString(Creating),
 
635
                  FnFilter1(G.filename)));
 
636
            }
 
637
 
 
638
            /* set file attributes: */
 
639
            z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
 
640
 
 
641
            /* set dir time (note trailing '/') */
 
642
            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
 
643
        } else if (IS_OVERWRT_ALL) {
 
644
            /* overwrite attributes of existing directory on user's request */
 
645
 
 
646
            /* set file attributes: */
 
647
            z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
 
648
        }
 
649
        /* dir existed already; don't look for data to extract */
 
650
        return (error & ~MPN_MASK) | MPN_INF_SKIP;
 
651
    }
 
652
 
 
653
    *pp = '\0';                   /* done with pathcomp:  terminate it */
 
654
 
 
655
    /* if not saving them, remove VMS version numbers (appended ";###") */
 
656
    if (!uO.V_flag && lastsemi) {
 
657
#ifndef MAYBE_PLAIN_FAT
 
658
        pp = lastsemi + 1;
 
659
#else
 
660
# ifdef USE_LFN
 
661
        if (use_lfn)
 
662
            pp = lastsemi + 1;
 
663
        else
 
664
            pp = lastsemi;        /* semi-colon was omitted:  expect all #'s */
 
665
# else
 
666
        pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
 
667
# endif
 
668
#endif
 
669
        while (isdigit((uch)(*pp)))
 
670
            ++pp;
 
671
        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
 
672
            *lastsemi = '\0';
 
673
    }
 
674
 
 
675
#ifdef MAYBE_PLAIN_FAT
 
676
    maskDOSdevice(__G__ pathcomp, last_dot);
 
677
#else
 
678
    maskDOSdevice(__G__ pathcomp, NULL);
 
679
#endif
 
680
 
 
681
    if (G.pInfo->vollabel) {
 
682
        if (strlen(pathcomp) > 11)
 
683
            pathcomp[11] = '\0';
 
684
    } else {
 
685
#ifdef MAYBE_PLAIN_FAT
 
686
# ifdef USE_LFN
 
687
        if (!use_lfn)
 
688
            map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
 
689
# else
 
690
        map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
 
691
# endif
 
692
#endif
 
693
    }
 
694
 
 
695
    if (*pathcomp == '\0') {
 
696
        Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
 
697
          FnFilter1(G.filename)));
 
698
        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
 
699
    }
 
700
 
 
701
    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
 
702
    checkdir(__G__ G.filename, GETPATH);
 
703
 
 
704
    if (G.pInfo->vollabel) {    /* set the volume label now */
 
705
        if (QCOND2)
 
706
            Info(slide, 0, ((char *)slide, LoadFarString(Labelling),
 
707
              (nLabelDrive + 'a' - 1),
 
708
              FnFilter1(G.filename)));
 
709
        if (volumelabel(G.filename)) {
 
710
            Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel)));
 
711
            return (error & ~MPN_MASK) | MPN_ERR_SKIP;
 
712
        }
 
713
        /* success:  skip the "extraction" quietly */
 
714
        return (error & ~MPN_MASK) | MPN_INF_SKIP;
 
715
    }
 
716
 
 
717
    return error;
 
718
 
 
719
} /* end function mapname() */
 
720
 
 
721
 
 
722
 
 
723
 
 
724
 
 
725
/****************************/
 
726
/* Function maskDOSdevice() */
 
727
/****************************/
 
728
 
 
729
static void maskDOSdevice(__G__ pathcomp, last_dot)
 
730
    __GDEF
 
731
    char *pathcomp, *last_dot;
 
732
{
 
733
/*---------------------------------------------------------------------------
 
734
    Put an underscore in front of the file name if the file name is a
 
735
    DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to
 
736
    extract such a file would fail at best and wedge us at worst.
 
737
  ---------------------------------------------------------------------------*/
 
738
#if !defined(S_IFCHR) && defined(_S_IFCHR)
 
739
#  define S_IFCHR _S_IFCHR
 
740
#endif
 
741
#if !defined(S_ISCHR)
 
742
# if defined(_S_ISCHR)
 
743
#  define S_ISCHR(m) _S_ISCHR(m)
 
744
# elif defined(S_IFCHR)
 
745
#  define S_ISCHR(m) ((m) & S_IFCHR)
 
746
# endif
 
747
#endif
 
748
 
 
749
#ifdef DEBUG
 
750
    if (stat(pathcomp, &G.statbuf) == 0) {
 
751
        Trace((stderr,
 
752
               "maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n",
 
753
               FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode));
 
754
    } else {
 
755
        Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n",
 
756
               FnFilter1(pathcomp)));
 
757
    }
 
758
#endif
 
759
    if (stat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) {
 
760
        extent i;
 
761
 
 
762
        /* pathcomp contains a name of a DOS character device (builtin or
 
763
         * installed device driver).
 
764
         * Prepend a '_' to allow creation of the item in the file system.
 
765
         */
 
766
        for (i = strlen(pathcomp) + 1; i > 0; --i)
 
767
            pathcomp[i] = pathcomp[i - 1];
 
768
        pathcomp[0] = '_';
 
769
        if (last_dot != (char *)NULL)
 
770
            last_dot++;
 
771
    }
 
772
} /* end function maskDOSdevice() */
 
773
 
 
774
 
 
775
 
 
776
 
 
777
 
 
778
#ifdef MAYBE_PLAIN_FAT
 
779
 
 
780
/**********************/
 
781
/* Function map2fat() */
 
782
/**********************/
 
783
 
 
784
static void map2fat(pathcomp, last_dot)
 
785
    char *pathcomp, *last_dot;
 
786
{
 
787
    char *pEnd = pathcomp + strlen(pathcomp);
 
788
 
 
789
/*---------------------------------------------------------------------------
 
790
    Case 1:  filename has no dot, so figure out if we should add one.  Note
 
791
    that the algorithm does not try to get too fancy:  if there are no dots
 
792
    already, the name either gets truncated at 8 characters or the last un-
 
793
    derscore is converted to a dot (only if more characters are saved that
 
794
    way).  In no case is a dot inserted between existing characters.
 
795
 
 
796
              GRR:  have problem if filename is volume label??
 
797
 
 
798
  ---------------------------------------------------------------------------*/
 
799
 
 
800
    if (last_dot == (char *)NULL) {   /* no dots:  check for underscores... */
 
801
        char *plu = strrchr(pathcomp, '_');   /* pointer to last underscore */
 
802
 
 
803
        if ((plu != (char *)NULL) &&    /* found underscore: convert to dot? */
 
804
            (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8)) {
 
805
            last_dot = plu;       /* be lazy:  drop through to next if-block */
 
806
        } else if ((pEnd - pathcomp) > 8)
 
807
            /* no underscore; or converting underscore to dot would save less
 
808
               chars than leaving everything in the basename */
 
809
            pathcomp[8] = '\0';     /* truncate at 8 chars */
 
810
        /* else whole thing fits into 8 chars or less:  no change */
 
811
    }
 
812
 
 
813
/*---------------------------------------------------------------------------
 
814
    Case 2:  filename has dot in it, so truncate first half at 8 chars (shift
 
815
    extension if necessary) and second half at three.
 
816
  ---------------------------------------------------------------------------*/
 
817
 
 
818
    if (last_dot != (char *)NULL) {     /* one dot is OK: */
 
819
        *last_dot = '.';                /* put the last back in */
 
820
 
 
821
        if ((last_dot - pathcomp) > 8) {
 
822
            char *p, *q;
 
823
            int i;
 
824
 
 
825
            p = last_dot;
 
826
            q = last_dot = pathcomp + 8;
 
827
            for (i = 0;  (i < 4) && *p;  ++i) /* too many chars in basename: */
 
828
                *q++ = *p++;                  /*  shift extension left and */
 
829
            *q = '\0';                        /*  truncate/terminate it */
 
830
        } else if ((pEnd - last_dot) > 4)
 
831
            last_dot[4] = '\0';               /* too many chars in extension */
 
832
        /* else filename is fine as is:  no change */
 
833
 
 
834
        if ((last_dot - pathcomp) > 0 && last_dot[-1] == ' ')
 
835
            last_dot[-1] = '_';               /* NO blank in front of '.'! */
 
836
    }
 
837
} /* end function map2fat() */
 
838
 
 
839
#endif /* MAYBE_PLAIN_FAT */
 
840
 
 
841
 
 
842
 
 
843
 
 
844
 
 
845
/***********************/
 
846
/* Function checkdir() */
 
847
/***********************/
 
848
 
 
849
int checkdir(__G__ pathcomp, flag)
 
850
    __GDEF
 
851
    char *pathcomp;
 
852
    int flag;
 
853
/*
 
854
 * returns:
 
855
 *  MPN_OK          - no problem detected
 
856
 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
 
857
 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
 
858
 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
 
859
 *                    exists and is not a directory, but is supposed to be
 
860
 *  MPN_ERR_TOOLONG - path is too long
 
861
 *  MPN_NOMEM       - can't allocate memory for filename buffers
 
862
 */
 
863
{
 
864
    static int rootlen = 0;   /* length of rootpath */
 
865
    static char *rootpath;    /* user's "extract-to" directory */
 
866
    static char *buildpath;   /* full path (so far) to extracted file */
 
867
    static char *end;         /* pointer to end of buildpath ('\0') */
 
868
#ifdef MSC
 
869
    int attrs;                /* work around MSC stat() bug */
 
870
#endif
 
871
 
 
872
#   define FN_MASK   7
 
873
#   define FUNCTION  (flag & FN_MASK)
 
874
 
 
875
 
 
876
 
 
877
/*---------------------------------------------------------------------------
 
878
    APPEND_DIR:  append the path component to the path being built and check
 
879
    for its existence.  If doesn't exist and we are creating directories, do
 
880
    so for this one; else signal success or error as appropriate.
 
881
  ---------------------------------------------------------------------------*/
 
882
 
 
883
    if (FUNCTION == APPEND_DIR) {
 
884
        int too_long = FALSE;
 
885
 
 
886
        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
 
887
        while ((*end = *pathcomp++) != '\0')
 
888
            ++end;
 
889
 
 
890
        /* GRR:  could do better check, see if overrunning buffer as we go:
 
891
         * check end-buildpath after each append, set warning variable if
 
892
         * within 20 of FILNAMSIZ; then if var set, do careful check when
 
893
         * appending.  Clear variable when begin new path. */
 
894
 
 
895
        if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
 
896
            too_long = TRUE;                /* check if extracting directory? */
 
897
#ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
 
898
        if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf))
 
899
#else
 
900
        if (SSTAT(buildpath, &G.statbuf))   /* path doesn't exist */
 
901
#endif
 
902
        {
 
903
            if (!G.create_dirs) { /* told not to create (freshening) */
 
904
                free(buildpath);
 
905
                /* path doesn't exist:  nothing to do */
 
906
                return MPN_INF_SKIP;
 
907
            }
 
908
            if (too_long) {
 
909
                Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
 
910
                  FnFilter1(buildpath)));
 
911
                free(buildpath);
 
912
                /* no room for filenames:  fatal */
 
913
                return MPN_ERR_TOOLONG;
 
914
            }
 
915
            if (MKDIR(buildpath, 0777) == -1) {   /* create the directory */
 
916
                Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
 
917
                  FnFilter2(buildpath), FnFilter1(G.filename)));
 
918
                free(buildpath);
 
919
                /* path didn't exist, tried to create, failed */
 
920
                return MPN_ERR_SKIP;
 
921
            }
 
922
            created_dir = TRUE;
 
923
        } else if (!S_ISDIR(G.statbuf.st_mode)) {
 
924
            Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
 
925
              FnFilter2(buildpath), FnFilter1(G.filename)));
 
926
            free(buildpath);
 
927
            /* path existed but wasn't dir */
 
928
            return MPN_ERR_SKIP;
 
929
        }
 
930
        if (too_long) {
 
931
            Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
 
932
              FnFilter1(buildpath)));
 
933
            free(buildpath);
 
934
            /* no room for filenames:  fatal */
 
935
            return MPN_ERR_TOOLONG;
 
936
        }
 
937
        *end++ = '/';
 
938
        *end = '\0';
 
939
        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
 
940
        return MPN_OK;
 
941
 
 
942
    } /* end if (FUNCTION == APPEND_DIR) */
 
943
 
 
944
/*---------------------------------------------------------------------------
 
945
    GETPATH:  copy full path to the string pointed at by pathcomp, and free
 
946
    buildpath.
 
947
  ---------------------------------------------------------------------------*/
 
948
 
 
949
    if (FUNCTION == GETPATH) {
 
950
        strcpy(pathcomp, buildpath);
 
951
        Trace((stderr, "getting and freeing path [%s]\n",
 
952
          FnFilter1(pathcomp)));
 
953
        free(buildpath);
 
954
        buildpath = end = (char *)NULL;
 
955
        return MPN_OK;
 
956
    }
 
957
 
 
958
/*---------------------------------------------------------------------------
 
959
    APPEND_NAME:  assume the path component is the filename; append it and
 
960
    return without checking for existence.
 
961
  ---------------------------------------------------------------------------*/
 
962
 
 
963
    if (FUNCTION == APPEND_NAME) {
 
964
#ifdef NOVELL_BUG_WORKAROUND
 
965
        if (end == buildpath && !G.pInfo->vollabel) {
 
966
            /* work-around for Novell's "overwriting executables" bug:
 
967
               prepend "./" to name when no path component is specified */
 
968
            *end++ = '.';
 
969
            *end++ = '/';
 
970
        }
 
971
#endif /* NOVELL_BUG_WORKAROUND */
 
972
        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
 
973
        while ((*end = *pathcomp++) != '\0') {
 
974
            ++end;
 
975
            if ((end-buildpath) >= FILNAMSIZ) {
 
976
                *--end = '\0';
 
977
                Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
 
978
                  FnFilter1(G.filename), FnFilter2(buildpath)));
 
979
                return MPN_INF_TRUNC;   /* filename truncated */
 
980
            }
 
981
        }
 
982
        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
 
983
        /* could check for existence here, prompt for new name... */
 
984
        return MPN_OK;
 
985
    }
 
986
 
 
987
/*---------------------------------------------------------------------------
 
988
    INIT:  allocate and initialize buffer space for the file currently being
 
989
    extracted.  If file was renamed with an absolute path, don't prepend the
 
990
    extract-to path.
 
991
  ---------------------------------------------------------------------------*/
 
992
 
 
993
    if (FUNCTION == INIT) {
 
994
        Trace((stderr, "initializing buildpath to "));
 
995
        /* allocate space for full filename, root path, and maybe "./" */
 
996
        if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
 
997
            (char *)NULL)
 
998
            return MPN_NOMEM;
 
999
        if (G.pInfo->vollabel) {
 
1000
/* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
 
1001
            if (renamed_fullpath && pathcomp[1] == ':')
 
1002
                *buildpath = (char)ToLower(*pathcomp);
 
1003
            else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':')
 
1004
                *buildpath = (char)ToLower(*rootpath);
 
1005
            else {
 
1006
                GETDRIVE(nLabelDrive);   /* assumed that a == 1, b == 2, etc. */
 
1007
                *buildpath = (char)(nLabelDrive - 1 + 'a');
 
1008
            }
 
1009
            nLabelDrive = *buildpath - 'a' + 1;        /* save for mapname() */
 
1010
            if (uO.volflag == 0 || *buildpath < 'a' || /* no label/bogus disk */
 
1011
               (uO.volflag == 1 && !isfloppy(nLabelDrive))) /* -$:  no fixed */
 
1012
            {
 
1013
                free(buildpath);
 
1014
                return MPN_VOL_LABEL;    /* skipping with message */
 
1015
            }
 
1016
            *buildpath = '\0';
 
1017
            end = buildpath;
 
1018
        } else if (renamed_fullpath) {   /* pathcomp = valid data */
 
1019
            end = buildpath;
 
1020
            while ((*end = *pathcomp++) != '\0')
 
1021
                ++end;
 
1022
        } else if (rootlen > 0) {
 
1023
            strcpy(buildpath, rootpath);
 
1024
            end = buildpath + rootlen;
 
1025
        } else {
 
1026
            *buildpath = '\0';
 
1027
            end = buildpath;
 
1028
        }
 
1029
        Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
 
1030
        return MPN_OK;
 
1031
    }
 
1032
 
 
1033
/*---------------------------------------------------------------------------
 
1034
    ROOT:  if appropriate, store the path in rootpath and create it if neces-
 
1035
    sary; else assume it's a zipfile member and return.  This path segment
 
1036
    gets used in extracting all members from every zipfile specified on the
 
1037
    command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
 
1038
    directory specification includes a drive letter (leading "x:"), it is
 
1039
    treated just as if it had a trailing '/'--that is, one directory level
 
1040
    will be created if the path doesn't exist, unless this is otherwise pro-
 
1041
    hibited (e.g., freshening).
 
1042
  ---------------------------------------------------------------------------*/
 
1043
 
 
1044
#if (!defined(SFX) || defined(SFX_EXDIR))
 
1045
    if (FUNCTION == ROOT) {
 
1046
        Trace((stderr, "initializing root path to [%s]\n",
 
1047
          FnFilter1(pathcomp)));
 
1048
        if (pathcomp == (char *)NULL) {
 
1049
            rootlen = 0;
 
1050
            return MPN_OK;
 
1051
        }
 
1052
        if (rootlen > 0)        /* rootpath was already set, nothing to do */
 
1053
            return MPN_OK;
 
1054
        if ((rootlen = strlen(pathcomp)) > 0) {
 
1055
            int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
 
1056
            char *tmproot;
 
1057
 
 
1058
            if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
 
1059
                rootlen = 0;
 
1060
                return MPN_NOMEM;
 
1061
            }
 
1062
            strcpy(tmproot, pathcomp);
 
1063
            if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
 
1064
                has_drive = TRUE;   /* drive designator */
 
1065
            if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
 
1066
                tmproot[--rootlen] = '\0';
 
1067
                had_trailing_pathsep = TRUE;
 
1068
            }
 
1069
            if (has_drive && (rootlen == 2)) {
 
1070
                if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
 
1071
                    add_dot = TRUE;    /* relative path: add '.' before '/' */
 
1072
            } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
 
1073
#ifdef MSC
 
1074
                /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
 
1075
                if (_dos_getfileattr(tmproot, &attrs) ||
 
1076
                    SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
 
1077
#else
 
1078
                if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
 
1079
#endif
 
1080
                {
 
1081
                    /* path does not exist */
 
1082
                    if (!G.create_dirs /* || iswild(tmproot) */ ) {
 
1083
                        free(tmproot);
 
1084
                        rootlen = 0;
 
1085
                        /* treat as stored file */
 
1086
                        return MPN_INF_SKIP;
 
1087
                    }
 
1088
/* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
 
1089
 * treat as stored file(s) */
 
1090
                    /* create directory (could add loop here scanning tmproot
 
1091
                     * to create more than one level, but really necessary?) */
 
1092
                    if (MKDIR(tmproot, 0777) == -1) {
 
1093
                        Info(slide, 1, ((char *)slide,
 
1094
                          LoadFarString(CantCreateExtractDir),
 
1095
                          FnFilter1(tmproot)));
 
1096
                        free(tmproot);
 
1097
                        rootlen = 0;
 
1098
                        /* path didn't exist, tried to create, failed: */
 
1099
                        /* file exists, or need 2+ subdir levels */
 
1100
                        return MPN_ERR_SKIP;
 
1101
                    }
 
1102
                }
 
1103
            }
 
1104
            if (add_dot)                    /* had just "x:", make "x:." */
 
1105
                tmproot[rootlen++] = '.';
 
1106
            tmproot[rootlen++] = '/';
 
1107
            tmproot[rootlen] = '\0';
 
1108
            if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
 
1109
                free(tmproot);
 
1110
                rootlen = 0;
 
1111
                return MPN_NOMEM;
 
1112
            }
 
1113
            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
 
1114
        }
 
1115
        return MPN_OK;
 
1116
    }
 
1117
#endif /* !SFX || SFX_EXDIR */
 
1118
 
 
1119
/*---------------------------------------------------------------------------
 
1120
    END:  free rootpath, immediately prior to program exit.
 
1121
  ---------------------------------------------------------------------------*/
 
1122
 
 
1123
    if (FUNCTION == END) {
 
1124
        Trace((stderr, "freeing rootpath\n"));
 
1125
        if (rootlen > 0) {
 
1126
            free(rootpath);
 
1127
            rootlen = 0;
 
1128
        }
 
1129
        return MPN_OK;
 
1130
    }
 
1131
 
 
1132
    return MPN_INVALID; /* should never reach */
 
1133
 
 
1134
} /* end function checkdir() */
 
1135
 
 
1136
 
 
1137
 
 
1138
 
 
1139
 
 
1140
 
 
1141
/***********************/
 
1142
/* Function isfloppy() */
 
1143
/***********************/
 
1144
 
 
1145
static int isfloppy(nDrive)  /* more precisely, is it removable? */
 
1146
    int nDrive;
 
1147
{
 
1148
    union REGS regs;
 
1149
 
 
1150
    regs.h.ah = 0x44;
 
1151
    regs.h.al = 0x08;
 
1152
    regs.h.bl = (uch)nDrive;
 
1153
#ifdef __EMX__
 
1154
    _int86(0x21, &regs, &regs);
 
1155
    if (WREGS(regs,flags) & 1)
 
1156
#else
 
1157
    intdos(&regs, &regs);
 
1158
    if (WREGS(regs,cflag))        /* error:  do default a/b check instead */
 
1159
#endif
 
1160
    {
 
1161
        Trace((stderr,
 
1162
          "error in DOS function 0x44 (AX = 0x%04x):  guessing instead...\n",
 
1163
          (unsigned int)(WREGS(regs,ax))));
 
1164
        return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
 
1165
    } else
 
1166
        return WREGS(regs,ax)? FALSE : TRUE;
 
1167
}
 
1168
 
 
1169
 
 
1170
 
 
1171
 
 
1172
/**************************/
 
1173
/* Function z_dos_chmod() */
 
1174
/**************************/
 
1175
 
 
1176
static int z_dos_chmod(__G__ fname, attributes)
 
1177
    __GDEF
 
1178
    ZCONST char *fname;
 
1179
    int attributes;
 
1180
{
 
1181
    char *name;
 
1182
    unsigned fnamelength;
 
1183
    int errv;
 
1184
 
 
1185
    /* set file attributes:
 
1186
       The DOS `chmod' system call requires to mask out the
 
1187
       directory and volume_label attribute bits.
 
1188
       And, a trailing '/' has to be removed from the directory name,
 
1189
       the DOS `chmod' system call does not accept it. */
 
1190
    fnamelength = strlen(fname);
 
1191
    if (fnamelength > 1 && fname[fnamelength-1] == '/' &&
 
1192
        fname[fnamelength-2] != ':' &&
 
1193
        (name = (char *)malloc(fnamelength)) != (char *)NULL) {
 
1194
        strncpy(name, fname, fnamelength-1);
 
1195
        name[fnamelength-1] = '\0';
 
1196
    } else {
 
1197
        name = (char *)fname;
 
1198
        fnamelength = 0;
 
1199
    }
 
1200
 
 
1201
#if defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >= 2))
 
1202
#   if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
 
1203
#     define Chmod  _rtl_chmod
 
1204
#   else
 
1205
#     define Chmod  _chmod
 
1206
#   endif
 
1207
    errv = (Chmod(name, 1, attributes & (~FSUBDIR & ~FVOLID)) !=
 
1208
            (attributes & (~FSUBDIR & ~FVOLID)));
 
1209
#   undef Chmod
 
1210
#else /* !(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
 
1211
    errv = (_dos_setfileattr(name, attributes & (~FSUBDIR & ~FVOLID)) != 0);
 
1212
#endif /* ?(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
 
1213
    if (errv)
 
1214
        Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong)));
 
1215
 
 
1216
    if (fnamelength > 0)
 
1217
        free(name);
 
1218
    return errv;
 
1219
} /* end function z_dos_chmod() */
 
1220
 
 
1221
 
 
1222
 
 
1223
 
 
1224
#if (!defined(__GO32__) && !defined(__EMX__))
 
1225
 
 
1226
typedef struct dosfcb {
 
1227
    uch  flag;        /* ff to indicate extended FCB */
 
1228
    char res[5];      /* reserved */
 
1229
    uch  vattr;       /* attribute */
 
1230
    uch  drive;       /* drive (1=A, 2=B, ...) */
 
1231
    uch  vn[11];      /* file or volume name */
 
1232
    char dmmy[5];
 
1233
    uch  nn[11];      /* holds new name if renaming (else reserved) */
 
1234
    char dmmy2[9];
 
1235
} dos_fcb;
 
1236
 
 
1237
/**************************/
 
1238
/* Function volumelabel() */
 
1239
/**************************/
 
1240
 
 
1241
static int volumelabel(newlabel)
 
1242
    ZCONST char *newlabel;
 
1243
{
 
1244
#ifdef DEBUG
 
1245
    char *p;
 
1246
#endif
 
1247
    int len = strlen(newlabel);
 
1248
    int fcbseg, dtaseg, fcboff, dtaoff, retv;
 
1249
    dos_fcb  fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
 
1250
    struct SREGS sregs;
 
1251
    union REGS regs;
 
1252
 
 
1253
 
 
1254
/*---------------------------------------------------------------------------
 
1255
    Label the diskette specified by nLabelDrive using FCB calls.  (Old ver-
 
1256
    sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
 
1257
    labels.)  Must use far pointers for MSC FP_* macros to work; must pad
 
1258
    FCB filenames with spaces; and cannot include dot in 8th position.  May
 
1259
    or may not need to zero out FCBs before using; do so just in case.
 
1260
  ---------------------------------------------------------------------------*/
 
1261
 
 
1262
#ifdef WATCOMC_386
 
1263
    int truseg;
 
1264
 
 
1265
    memset(&sregs, 0, sizeof(sregs));
 
1266
    memset(&regs, 0, sizeof(regs));
 
1267
    /* PMODE/W does not support extended versions of any dos FCB functions, */
 
1268
    /* so we have to use brute force, allocating real mode memory for them. */
 
1269
    regs.w.ax = 0x0100;
 
1270
    regs.w.bx = (2 * sizeof(dos_fcb) + 15) >> 4;   /* size in paragraphs */
 
1271
    int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
 
1272
    if (regs.w.cflag)
 
1273
        return DF_MDY;                     /* no memory, return default */
 
1274
    truseg = regs.w.dx;                    /* protected mode selector */
 
1275
    dtaseg = regs.w.ax;                    /* real mode paragraph */
 
1276
    fcboff = 0;
 
1277
    dtaoff = sizeof(dos_fcb);
 
1278
#ifdef XXX__MK_FP_IS_BROKEN
 
1279
    /* XXX  This code may not be trustworthy in general, though it is   */
 
1280
    /* valid with DOS/4GW and PMODE/w, which is all we support for now. */
 
1281
    regs.w.ax = 6;
 
1282
    regs.w.bx = truseg;
 
1283
    int386(0x31, &regs, &regs);            /* convert seg to linear address */
 
1284
    pfcb = (dos_fcb far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
 
1285
    /* pfcb = (dos_fcb far *) ((ulg) dtaseg << 4); */
 
1286
    pdta = pfcb + 1;
 
1287
#else
 
1288
    pfcb = MK_FP(truseg, fcboff);
 
1289
    pdta = MK_FP(truseg, dtaoff);
 
1290
#endif
 
1291
    _fmemset((char far *)pfcb, 0, 2 * sizeof(dos_fcb));
 
1292
    /* we pass the REAL MODE paragraph to the dos interrupts: */
 
1293
    fcbseg = dtaseg;
 
1294
 
 
1295
#else /* !WATCOMC_386 */
 
1296
 
 
1297
    memset((char *)&dta, 0, sizeof(dos_fcb));
 
1298
    memset((char *)&fcb, 0, sizeof(dos_fcb));
 
1299
    fcbseg = FP_SEG(pfcb);
 
1300
    fcboff = FP_OFF(pfcb);
 
1301
    dtaseg = FP_SEG(pdta);
 
1302
    dtaoff = FP_OFF(pdta);
 
1303
#endif /* ?WATCOMC_386 */
 
1304
 
 
1305
#ifdef DEBUG
 
1306
    for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
 
1307
        if (*p)
 
1308
            fprintf(stderr, "error:  dta[%d] = %x\n", (p - (char *)&dta), *p);
 
1309
    for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
 
1310
        if (*p)
 
1311
            fprintf(stderr, "error:  fcb[%d] = %x\n", (p - (char *)&fcb), *p);
 
1312
    printf("testing pointer macros:\n");
 
1313
    segread(&sregs);
 
1314
    printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es,
 
1315
      sregs.ss);
 
1316
#endif /* DEBUG */
 
1317
 
 
1318
#if 0
 
1319
#ifdef __TURBOC__
 
1320
    bdosptr(0x1a, dta, DO_NOT_CARE);
 
1321
#else
 
1322
    (intdosx method below)
 
1323
#endif
 
1324
#endif /* 0 */
 
1325
 
 
1326
    /* set the disk transfer address for subsequent FCB calls */
 
1327
    sregs.ds = dtaseg;
 
1328
    WREGS(regs,dx) = dtaoff;
 
1329
    Trace((stderr, "segment:offset of pdta = %x:%x\n", dtaseg, dtaoff));
 
1330
    Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta));
 
1331
    regs.h.ah = 0x1a;
 
1332
    F_intdosx(&regs, &regs, &sregs);
 
1333
 
 
1334
    /* fill in the FCB */
 
1335
    sregs.ds = fcbseg;
 
1336
    WREGS(regs,dx) = fcboff;
 
1337
    pfcb->flag = 0xff;          /* extended FCB */
 
1338
    pfcb->vattr = 0x08;         /* attribute:  disk volume label */
 
1339
    pfcb->drive = (uch)nLabelDrive;
 
1340
 
 
1341
#ifdef DEBUG
 
1342
    Trace((stderr, "segment:offset of pfcb = %x:%x\n",
 
1343
      (unsigned int)(sregs.ds),
 
1344
      (unsigned int)(WREGS(regs,dx))));
 
1345
    Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb));
 
1346
    Trace((stderr, "(2nd check:  labelling drive %c:)\n", pfcb->drive-1+'A'));
 
1347
    if (pfcb->flag != fcb.flag)
 
1348
        fprintf(stderr, "error:  pfcb->flag = %d, fcb.flag = %d\n",
 
1349
          pfcb->flag, fcb.flag);
 
1350
    if (pfcb->drive != fcb.drive)
 
1351
        fprintf(stderr, "error:  pfcb->drive = %d, fcb.drive = %d\n",
 
1352
          pfcb->drive, fcb.drive);
 
1353
    if (pfcb->vattr != fcb.vattr)
 
1354
        fprintf(stderr, "error:  pfcb->vattr = %d, fcb.vattr = %d\n",
 
1355
          pfcb->vattr, fcb.vattr);
 
1356
#endif /* DEBUG */
 
1357
 
 
1358
    /* check for existing label */
 
1359
    Trace((stderr, "searching for existing label via FCBs\n"));
 
1360
    regs.h.ah = 0x11;      /* FCB find first */
 
1361
#ifdef WATCOMC_386
 
1362
    _fstrncpy((char far *)&pfcb->vn, "???????????", 11);
 
1363
#else
 
1364
    strncpy((char *)fcb.vn, "???????????", 11);   /* i.e., "*.*" */
 
1365
#endif /* ?WATCOMC_386 */
 
1366
    Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn));
 
1367
    Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n",
 
1368
      (unsigned int)(regs.h.ah), (unsigned int)(WREGS(regs,dx)),
 
1369
      (unsigned int)(sregs.ds)));
 
1370
    Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
 
1371
      fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
 
1372
    F_intdosx(&regs, &regs, &sregs);
 
1373
 
 
1374
/*---------------------------------------------------------------------------
 
1375
    If not previously labelled, write a new label.  Otherwise just rename,
 
1376
    since MS-DOS 2.x has a bug that damages the FAT when the old label is
 
1377
    deleted.
 
1378
  ---------------------------------------------------------------------------*/
 
1379
 
 
1380
    if (regs.h.al) {
 
1381
        Trace((stderr, "no label found\n\n"));
 
1382
        regs.h.ah = 0x16;                 /* FCB create file */
 
1383
#ifdef WATCOMC_386
 
1384
        _fstrncpy((char far *)pfcb->vn, newlabel, len);
 
1385
        if (len < 11)
 
1386
            _fstrncpy((char far *)(pfcb->vn+len), "           ", 11-len);
 
1387
#else
 
1388
        strncpy((char *)fcb.vn, newlabel, len);
 
1389
        if (len < 11)   /* fill with spaces */
 
1390
            strncpy((char *)(fcb.vn+len), "           ", 11-len);
 
1391
#endif
 
1392
        Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
 
1393
          (ulg)pfcb->vn));
 
1394
        Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
 
1395
          fcb.drive, fcb.vattr));
 
1396
        Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
 
1397
        F_intdosx(&regs, &regs, &sregs);
 
1398
        regs.h.ah = 0x10;                 /* FCB close file */
 
1399
        if (regs.h.al) {
 
1400
            Trace((stderr, "unable to write volume name (AL = %x)\n",
 
1401
              (unsigned int)(regs.h.al)));
 
1402
            F_intdosx(&regs, &regs, &sregs);
 
1403
            retv = 1;
 
1404
        } else {
 
1405
            F_intdosx(&regs, &regs, &sregs);
 
1406
            Trace((stderr, "new volume label [%s] written\n", newlabel));
 
1407
            retv = 0;
 
1408
        }
 
1409
    } else {
 
1410
        Trace((stderr, "found old label [%s]\n\n", dta.vn));  /* not term. */
 
1411
        regs.h.ah = 0x17;                 /* FCB rename */
 
1412
#ifdef WATCOMC_386
 
1413
        _fstrncpy((char far *)pfcb->vn, (char far *)pdta->vn, 11);
 
1414
        _fstrncpy((char far *)pfcb->nn, newlabel, len);
 
1415
        if (len < 11)
 
1416
            _fstrncpy((char far *)(pfcb->nn+len), "           ", 11-len);
 
1417
#else
 
1418
        strncpy((char *)fcb.vn, (char *)dta.vn, 11);
 
1419
        strncpy((char *)fcb.nn, newlabel, len);
 
1420
        if (len < 11)                     /* fill with spaces */
 
1421
            strncpy((char *)(fcb.nn+len), "           ", 11-len);
 
1422
#endif
 
1423
        Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
 
1424
          (ulg)pfcb->vn));
 
1425
        Trace((stderr, "fcb.nn = %lx  pfcb->nn = %lx\n", (ulg)fcb.nn,
 
1426
          (ulg)pfcb->nn));
 
1427
        Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
 
1428
          fcb.drive, fcb.vattr));
 
1429
        Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
 
1430
        Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn));
 
1431
        F_intdosx(&regs, &regs, &sregs);
 
1432
        if (regs.h.al) {
 
1433
            Trace((stderr, "Unable to change volume name (AL = %x)\n",
 
1434
              (unsigned int)(regs.h.al)));
 
1435
            retv = 1;
 
1436
        } else {
 
1437
            Trace((stderr, "volume label changed to [%s]\n", newlabel));
 
1438
            retv = 0;
 
1439
        }
 
1440
    }
 
1441
#ifdef WATCOMC_386
 
1442
    regs.w.ax = 0x0101;                    /* free dos memory */
 
1443
    regs.w.dx = truseg;
 
1444
    int386(0x31, &regs, &regs);
 
1445
#endif
 
1446
    return retv;
 
1447
 
 
1448
} /* end function volumelabel() */
 
1449
 
 
1450
#endif /* !__GO32__ && !__EMX__ */
 
1451
 
 
1452
 
 
1453
 
 
1454
 
 
1455
 
 
1456
#if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP))
 
1457
/* The following DOS date/time structure is machine-dependent as it
 
1458
 * assumes "little-endian" byte order.  For MSDOS-specific code, which
 
1459
 * is run on ix86 CPUs (or emulators), this assumption is valid; but
 
1460
 * care should be taken when using this code as template for other ports.
 
1461
 */
 
1462
typedef union {
 
1463
    ulg z_dostime;
 
1464
# ifdef __TURBOC__
 
1465
    struct ftime ft;            /* system file time record */
 
1466
# endif
 
1467
    struct {                    /* date and time words */
 
1468
        ush ztime;              /* DOS file modification time word */
 
1469
        ush zdate;              /* DOS file modification date word */
 
1470
    } zft;
 
1471
    struct {                    /* DOS date/time components bitfield */
 
1472
        unsigned zt_se : 5;
 
1473
        unsigned zt_mi : 6;
 
1474
        unsigned zt_hr : 5;
 
1475
        unsigned zd_dy : 5;
 
1476
        unsigned zd_mo : 4;
 
1477
        unsigned zd_yr : 7;
 
1478
    } z_dtf;
 
1479
} dos_fdatetime;
 
1480
#endif /* USE_EF_UT_TIME || TIMESTAMP */
 
1481
 
 
1482
 
 
1483
/****************************/
 
1484
/* Function close_outfile() */
 
1485
/****************************/
 
1486
 
 
1487
void close_outfile(__G)
 
1488
    __GDEF
 
1489
 /*
 
1490
  * MS-DOS VERSION
 
1491
  *
 
1492
  * Set the output file date/time stamp according to information from the
 
1493
  * zipfile directory record for this member, then close the file and set
 
1494
  * its permissions (archive, hidden, read-only, system).  Aside from closing
 
1495
  * the file, this routine is optional (but most compilers support it).
 
1496
  */
 
1497
{
 
1498
#ifdef USE_EF_UT_TIME
 
1499
    dos_fdatetime dos_dt;
 
1500
    iztimes z_utime;
 
1501
    struct tm *t;
 
1502
#endif /* USE_EF_UT_TIME */
 
1503
 
 
1504
 
 
1505
/*---------------------------------------------------------------------------
 
1506
    Copy and/or convert time and date variables, if necessary; then set the
 
1507
    file time/date.  WEIRD BORLAND "BUG":  if output is buffered, and if run
 
1508
    under at least some versions of DOS (e.g., 6.0), and if files are smaller
 
1509
    than DOS physical block size (i.e., 512 bytes) (?), then files MAY NOT
 
1510
    get timestamped correctly--apparently setftime() occurs before any data
 
1511
    are written to the file, and when file is closed and buffers are flushed,
 
1512
    timestamp is overwritten with current time.  Even with a 32K buffer, this
 
1513
    does not seem to occur with larger files.  UnZip output is now unbuffered,
 
1514
    but if it were not, could still avoid problem by adding "fflush(outfile)"
 
1515
    just before setftime() call.  Weird, huh?
 
1516
  ---------------------------------------------------------------------------*/
 
1517
 
 
1518
#ifdef USE_EF_UT_TIME
 
1519
    if (G.extra_field &&
 
1520
#ifdef IZ_CHECK_TZ
 
1521
        G.tz_is_valid &&
 
1522
#endif
 
1523
        (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
 
1524
                          G.lrec.last_mod_dos_datetime, &z_utime, NULL)
 
1525
         & EB_UT_FL_MTIME))
 
1526
    {
 
1527
        TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
 
1528
          z_utime.mtime));
 
1529
        /* round up (down if "up" overflows) to even seconds */
 
1530
        if (z_utime.mtime & 1)
 
1531
            z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
 
1532
                             z_utime.mtime + 1 : z_utime.mtime - 1;
 
1533
        TIMET_TO_NATIVE(z_utime.mtime)   /* NOP unless MSC 7.0 or Macintosh */
 
1534
        t = localtime(&(z_utime.mtime));
 
1535
    } else
 
1536
        t = (struct tm *)NULL;
 
1537
    if (t != (struct tm *)NULL) {
 
1538
        if (t->tm_year < 80) {
 
1539
            dos_dt.z_dtf.zt_se = 0;
 
1540
            dos_dt.z_dtf.zt_mi = 0;
 
1541
            dos_dt.z_dtf.zt_hr = 0;
 
1542
            dos_dt.z_dtf.zd_dy = 1;
 
1543
            dos_dt.z_dtf.zd_mo = 1;
 
1544
            dos_dt.z_dtf.zd_yr = 0;
 
1545
        } else {
 
1546
            dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
 
1547
            dos_dt.z_dtf.zt_mi = t->tm_min;
 
1548
            dos_dt.z_dtf.zt_hr = t->tm_hour;
 
1549
            dos_dt.z_dtf.zd_dy = t->tm_mday;
 
1550
            dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
 
1551
            dos_dt.z_dtf.zd_yr = t->tm_year - 80;
 
1552
        }
 
1553
    } else {
 
1554
        dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
 
1555
    }
 
1556
# ifdef __TURBOC__
 
1557
    setftime(fileno(G.outfile), &dos_dt.ft);
 
1558
# else
 
1559
    _dos_setftime(fileno(G.outfile), dos_dt.zft.zdate, dos_dt.zft.ztime);
 
1560
# endif
 
1561
#else /* !USE_EF_UT_TIME */
 
1562
# ifdef __TURBOC__
 
1563
    setftime(fileno(G.outfile),
 
1564
             (struct ftime *)(&(G.lrec.last_mod_dos_datetime)));
 
1565
# else
 
1566
    _dos_setftime(fileno(G.outfile), (ush)(G.lrec.last_mod_dos_datetime >> 16),
 
1567
                                     (ush)(G.lrec.last_mod_dos_datetime));
 
1568
# endif
 
1569
#endif /* ?USE_EF_UT_TIME */
 
1570
 
 
1571
/*---------------------------------------------------------------------------
 
1572
    And finally we can close the file...at least everybody agrees on how to
 
1573
    do *this*.  I think...  Also change the mode according to the stored file
 
1574
    attributes, since we didn't do that when we opened the dude.
 
1575
  ---------------------------------------------------------------------------*/
 
1576
 
 
1577
    fclose(G.outfile);
 
1578
 
 
1579
    z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
 
1580
 
 
1581
} /* end function close_outfile() */
 
1582
 
 
1583
 
 
1584
 
 
1585
 
 
1586
 
 
1587
#ifdef TIMESTAMP
 
1588
 
 
1589
/*************************/
 
1590
/* Function stamp_file() */
 
1591
/*************************/
 
1592
 
 
1593
int stamp_file(fname, modtime)
 
1594
    ZCONST char *fname;
 
1595
    time_t modtime;
 
1596
{
 
1597
    dos_fdatetime dos_dt;
 
1598
    time_t t_even;
 
1599
    struct tm *t;
 
1600
    int fd;                             /* file handle */
 
1601
 
 
1602
    /* round up (down if "up" overflows) to even seconds */
 
1603
    t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
 
1604
    TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
 
1605
    t = localtime(&t_even);
 
1606
    if (t == (struct tm *)NULL)
 
1607
        return -1;                      /* time conversion error */
 
1608
    if (t->tm_year < 80) {
 
1609
        dos_dt.z_dtf.zt_se = 0;
 
1610
        dos_dt.z_dtf.zt_mi = 0;
 
1611
        dos_dt.z_dtf.zt_hr = 0;
 
1612
        dos_dt.z_dtf.zd_dy = 1;
 
1613
        dos_dt.z_dtf.zd_mo = 1;
 
1614
        dos_dt.z_dtf.zd_yr = 0;
 
1615
    } else {
 
1616
        dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
 
1617
        dos_dt.z_dtf.zt_mi = t->tm_min;
 
1618
        dos_dt.z_dtf.zt_hr = t->tm_hour;
 
1619
        dos_dt.z_dtf.zd_dy = t->tm_mday;
 
1620
        dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
 
1621
        dos_dt.z_dtf.zd_yr = t->tm_year - 80;
 
1622
    }
 
1623
    if (((fd = open((char *)fname, 0)) == -1) ||
 
1624
# ifdef __TURBOC__
 
1625
        (setftime(fd, &dos_dt.ft)))
 
1626
# else
 
1627
        (_dos_setftime(fd, dos_dt.zft.zdate, dos_dt.zft.ztime)))
 
1628
# endif
 
1629
    {
 
1630
        if (fd != -1)
 
1631
            close(fd);
 
1632
        return -1;
 
1633
    }
 
1634
    close(fd);
 
1635
    return 0;
 
1636
 
 
1637
} /* end function stamp_file() */
 
1638
 
 
1639
#endif /* TIMESTAMP */
 
1640
 
 
1641
 
 
1642
 
 
1643
 
 
1644
#ifndef SFX
 
1645
 
 
1646
/*************************/
 
1647
/* Function dateformat() */
 
1648
/*************************/
 
1649
 
 
1650
int dateformat()
 
1651
{
 
1652
 
 
1653
/*---------------------------------------------------------------------------
 
1654
    For those operating systems that support it, this function returns a
 
1655
    value that tells how national convention says that numeric dates are
 
1656
    displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
 
1657
    should be fairly obvious).
 
1658
  ---------------------------------------------------------------------------*/
 
1659
 
 
1660
#ifndef WINDLL
 
1661
    ush CountryInfo[18];
 
1662
#if (!defined(__GO32__) && !defined(__EMX__))
 
1663
    ush far *_CountryInfo = CountryInfo;
 
1664
    struct SREGS sregs;
 
1665
    union REGS regs;
 
1666
#ifdef WATCOMC_386
 
1667
    ush seg, para;
 
1668
 
 
1669
    memset(&sregs, 0, sizeof(sregs));
 
1670
    memset(&regs, 0, sizeof(regs));
 
1671
    /* PMODE/W does not support an extended version of dos function 38,   */
 
1672
    /* so we have to use brute force, allocating real mode memory for it. */
 
1673
    regs.w.ax = 0x0100;
 
1674
    regs.w.bx = 3;                         /* 36 bytes rounds up to 48 */
 
1675
    int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
 
1676
    if (regs.w.cflag)
 
1677
        return DF_MDY;                     /* no memory, return default */
 
1678
    seg = regs.w.dx;
 
1679
    para = regs.w.ax;
 
1680
 
 
1681
#ifdef XXX__MK_FP_IS_BROKEN
 
1682
    /* XXX  This code may not be trustworthy in general, though it is
 
1683
     * valid with DOS/4GW and PMODE/w, which is all we support for now. */
 
1684
 /* _CountryInfo = (ush far *) (para << 4); */ /* works for some extenders */
 
1685
    regs.w.ax = 6;
 
1686
    regs.w.bx = seg;
 
1687
    int386(0x31, &regs, &regs);            /* convert seg to linear address */
 
1688
    _CountryInfo = (ush far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
 
1689
#else
 
1690
    _CountryInfo = (ush far *) MK_FP(seg, 0);
 
1691
#endif
 
1692
 
 
1693
    sregs.ds = para;                       /* real mode paragraph */
 
1694
    regs.w.dx = 0;                         /* no offset from segment */
 
1695
    regs.w.ax = 0x3800;
 
1696
    int86x_realmode(0x21, &regs, &regs, &sregs);
 
1697
    CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
 
1698
    regs.w.ax = 0x0101;
 
1699
    regs.w.dx = seg;
 
1700
    int386(0x31, &regs, &regs);              /* DPMI free DOS memory */
 
1701
 
 
1702
#else /* !WATCOMC_386 */
 
1703
 
 
1704
    sregs.ds  = FP_SEG(_CountryInfo);
 
1705
    regs.x.dx = FP_OFF(_CountryInfo);
 
1706
    regs.x.ax = 0x3800;
 
1707
    intdosx(&regs, &regs, &sregs);
 
1708
#endif /* ?WATCOMC_386 */
 
1709
 
 
1710
#else /* __GO32__ || __EMX__ */
 
1711
    _dos_getcountryinfo(CountryInfo);
 
1712
#endif /* ?(__GO32__ || __EMX__) */
 
1713
 
 
1714
    switch(CountryInfo[0]) {
 
1715
        case 0:
 
1716
            return DF_MDY;
 
1717
        case 1:
 
1718
            return DF_DMY;
 
1719
        case 2:
 
1720
            return DF_YMD;
 
1721
    }
 
1722
#endif /* !WINDLL && !WATCOMC_386 */
 
1723
 
 
1724
    return DF_MDY;   /* default for systems without locale info */
 
1725
 
 
1726
} /* end function dateformat() */
 
1727
 
 
1728
 
 
1729
 
 
1730
 
 
1731
#ifndef WINDLL
 
1732
 
 
1733
/************************/
 
1734
/*  Function version()  */
 
1735
/************************/
 
1736
 
 
1737
void version(__G)
 
1738
    __GDEF
 
1739
{
 
1740
    int len;
 
1741
#if defined(__DJGPP__) || defined(__WATCOMC__) || \
 
1742
    (defined(_MSC_VER) && (_MSC_VER != 800))
 
1743
    char buf[80];
 
1744
#endif
 
1745
 
 
1746
    len = sprintf((char *)slide, LoadFarString(CompiledWith),
 
1747
 
 
1748
#if defined(__GNUC__)
 
1749
#  if defined(__DJGPP__)
 
1750
      (sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__), buf),
 
1751
#  elif defined(__GO32__)         /* __GO32__ is defined as "1" only (sigh) */
 
1752
      "djgpp v1.x / gcc ",
 
1753
#  elif defined(__EMX__)          /* ...so is __EMX__ (double sigh) */
 
1754
      "emx+gcc ",
 
1755
#  else
 
1756
      "gcc ",
 
1757
#  endif
 
1758
      __VERSION__,
 
1759
#elif defined(__WATCOMC__)
 
1760
#  if (__WATCOMC__ % 10 != 0)
 
1761
      "Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100,
 
1762
                               __WATCOMC__ % 100), buf),
 
1763
#  else
 
1764
      "Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100,
 
1765
                               (__WATCOMC__ % 100) / 10), buf),
 
1766
#  endif
 
1767
#elif defined(__TURBOC__)
 
1768
#  ifdef __BORLANDC__
 
1769
      "Borland C++",
 
1770
#    if (__BORLANDC__ < 0x0200)
 
1771
        " 1.0",
 
1772
#    elif (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
 
1773
        " 2.0",
 
1774
#    elif (__BORLANDC__ == 0x0400)
 
1775
        " 3.0",
 
1776
#    elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
 
1777
        " 3.1",
 
1778
#    elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
 
1779
        " 4.0 or 4.02",
 
1780
#    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
 
1781
        " 4.5",
 
1782
#    elif (__BORLANDC__ == 0x0500)
 
1783
        " 5.0",
 
1784
#    else
 
1785
        " later than 5.0",
 
1786
#    endif
 
1787
#  else
 
1788
      "Turbo C",
 
1789
#    if (__TURBOC__ > 0x0401)        /* Kevin:  3.0 -> 0x0401 */
 
1790
        "++ later than 3.0",
 
1791
#    elif (__TURBOC__ >= 0x0400)
 
1792
        "++ 3.0",
 
1793
#    elif (__TURBOC__ >= 0x0297)     /* see remark for Borland C++ 2.0 */
 
1794
        "++ 2.0",
 
1795
#    elif (__TURBOC__ == 0x0296)     /* [662] checked by SPC */
 
1796
        "++ 1.01",
 
1797
#    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
 
1798
        "++ 1.0",
 
1799
#    elif (__TURBOC__ == 0x0201)     /* Brian:  2.01 -> 0x0201 */
 
1800
        " 2.01",
 
1801
#    elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
 
1802
        " 2.0",
 
1803
#    elif (__TURBOC__ > 0x0100)
 
1804
        " 1.5",                      /* James:  0x0105? */
 
1805
#    else
 
1806
        " 1.0",                      /* James:  0x0100 */
 
1807
#    endif
 
1808
#  endif
 
1809
#elif defined(MSC)
 
1810
#  if defined(_QC) && !defined(_MSC_VER)
 
1811
      "MS Quick C ", "2.0 or earlier",      /* _QC is defined as 1 */
 
1812
#  elif defined(_QC) && (_MSC_VER == 600)
 
1813
      "MS Quick C ", "2.5 (MSC 6.00)",
 
1814
#  else
 
1815
      "Microsoft C ",
 
1816
#    ifdef _MSC_VER
 
1817
#      if (_MSC_VER == 800)
 
1818
        "8.0/8.0c (Visual C++ 1.0/1.5)",
 
1819
#      else
 
1820
        (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
 
1821
#      endif
 
1822
#    else
 
1823
      "5.1 or earlier",
 
1824
#    endif
 
1825
#  endif
 
1826
#else
 
1827
      "unknown compiler", "",
 
1828
#endif /* ?compilers */
 
1829
 
 
1830
      "\nMS-DOS",
 
1831
 
 
1832
#if (defined(__GNUC__) || defined(WATCOMC_386))
 
1833
      " (32-bit)",
 
1834
#else
 
1835
#  if defined(M_I86HM) || defined(__HUGE__)
 
1836
      " (16-bit, huge)",
 
1837
#  elif defined(M_I86LM) || defined(__LARGE__)
 
1838
      " (16-bit, large)",
 
1839
#  elif defined(M_I86MM) || defined(__MEDIUM__)
 
1840
      " (16-bit, medium)",
 
1841
#  elif defined(M_I86CM) || defined(__COMPACT__)
 
1842
      " (16-bit, compact)",
 
1843
#  elif defined(M_I86SM) || defined(__SMALL__)
 
1844
      " (16-bit, small)",
 
1845
#  elif defined(M_I86TM) || defined(__TINY__)
 
1846
      " (16-bit, tiny)",
 
1847
#  else
 
1848
      " (16-bit)",
 
1849
#  endif
 
1850
#endif
 
1851
 
 
1852
#ifdef __DATE__
 
1853
      " on ", __DATE__
 
1854
#else
 
1855
      "", ""
 
1856
#endif
 
1857
    );
 
1858
 
 
1859
    (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
 
1860
                                /* MSC can't handle huge macro expansion */
 
1861
 
 
1862
    /* temporary debugging code for Borland compilers only */
 
1863
#if (defined(__TURBOC__) && defined(DEBUG))
 
1864
    Info(slide, 0, ((char *)slide, "\tdebug(__TURBOC__ = 0x%04x = %d)\n",
 
1865
      __TURBOC__, __TURBOC__));
 
1866
#ifdef __BORLANDC__
 
1867
    Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ = 0x%04x)\n",
 
1868
      __BORLANDC__));
 
1869
#else
 
1870
    Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ not defined)\n"));
 
1871
#endif
 
1872
#ifdef __TCPLUSPLUS__
 
1873
    Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ = 0x%04x)\n",
 
1874
      __TCPLUSPLUS__));
 
1875
#else
 
1876
    Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ not defined)\n"));
 
1877
#endif
 
1878
#ifdef __BCPLUSPLUS__
 
1879
    Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n",
 
1880
      __BCPLUSPLUS__));
 
1881
#else
 
1882
    Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ not defined)\n\n"));
 
1883
#endif
 
1884
#endif /* __TURBOC__ && DEBUG */
 
1885
 
 
1886
} /* end function version() */
 
1887
 
 
1888
#endif /* !WINDLL */
 
1889
#endif /* !SFX */
 
1890
 
 
1891
#endif /* !FUNZIP */
 
1892
 
 
1893
 
 
1894
 
 
1895
 
 
1896
 
 
1897
#ifdef MY_ZCALLOC       /* Special zcalloc function for MEMORY16 (MSDOS/OS2) */
 
1898
 
 
1899
#if defined(__TURBOC__) && !defined(OS2)
 
1900
#include <alloc.h>
 
1901
/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
 
1902
 * and farmalloc(64K) returns a pointer with an offset of 8, so we
 
1903
 * must fix the pointer. Warning: the pointer must be put back to its
 
1904
 * original form in order to free it, use zcfree().
 
1905
 */
 
1906
 
 
1907
#define MAX_PTR 2       /* reduced from 10 to save space */
 
1908
/* 10*64K = 640K */
 
1909
 
 
1910
static int next_ptr = 0;
 
1911
 
 
1912
typedef struct ptr_table_s {
 
1913
    zvoid far *org_ptr;
 
1914
    zvoid far *new_ptr;
 
1915
} ptr_table;
 
1916
 
 
1917
static ptr_table table[MAX_PTR];
 
1918
/* This table is used to remember the original form of pointers
 
1919
 * to large buffers (64K). Such pointers are normalized with a zero offset.
 
1920
 * Since MSDOS is not a preemptive multitasking OS, this table is not
 
1921
 * protected from concurrent access. This hack doesn't work anyway on
 
1922
 * a protected system like OS/2. Use Microsoft C instead.
 
1923
 */
 
1924
 
 
1925
zvoid far *zcalloc(unsigned items, unsigned size)
 
1926
{
 
1927
    zvoid far *buf;
 
1928
    ulg bsize = (ulg)items*size;
 
1929
 
 
1930
    if (bsize < (65536L-16L)) {
 
1931
        buf = farmalloc(bsize);
 
1932
        if (*(ush*)&buf != 0) return buf;
 
1933
    } else {
 
1934
        buf = farmalloc(bsize + 16L);
 
1935
    }
 
1936
    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
 
1937
    table[next_ptr].org_ptr = buf;
 
1938
 
 
1939
    /* Normalize the pointer to seg:0 */
 
1940
    *((ush*)&buf+1) += ((ush)((uch*)buf-NULL) + 15) >> 4;
 
1941
    *(ush*)&buf = 0;
 
1942
    table[next_ptr++].new_ptr = buf;
 
1943
    return buf;
 
1944
}
 
1945
 
 
1946
zvoid zcfree(zvoid far *ptr)
 
1947
{
 
1948
    int n;
 
1949
    if (*(ush*)&ptr != 0) { /* object < 64K */
 
1950
        farfree(ptr);
 
1951
        return;
 
1952
    }
 
1953
    /* Find the original pointer */
 
1954
    for (n = next_ptr - 1; n >= 0; n--) {
 
1955
        if (ptr != table[n].new_ptr) continue;
 
1956
 
 
1957
        farfree(table[n].org_ptr);
 
1958
        while (++n < next_ptr) {
 
1959
            table[n-1] = table[n];
 
1960
        }
 
1961
        next_ptr--;
 
1962
        return;
 
1963
    }
 
1964
    Trace((stderr, "zcfree: ptr not found!\n"));
 
1965
}
 
1966
#endif /* __TURBOC__ */
 
1967
 
 
1968
#if defined(MSC) || defined(__WATCOMC__)
 
1969
#if (!defined(_MSC_VER) || (_MSC_VER < 700))
 
1970
#  define _halloc  halloc
 
1971
#  define _hfree   hfree
 
1972
#endif
 
1973
 
 
1974
zvoid far *zcalloc(unsigned items, unsigned size)
 
1975
{
 
1976
    return (zvoid far *)_halloc((long)items, size);
 
1977
}
 
1978
 
 
1979
zvoid zcfree(zvoid far *ptr)
 
1980
{
 
1981
    _hfree((void huge *)ptr);
 
1982
}
 
1983
#endif /* MSC || __WATCOMC__ */
 
1984
 
 
1985
#endif /* MY_ZCALLOC */
 
1986
 
 
1987
 
 
1988
#ifndef FUNZIP
 
1989
 
 
1990
#if (defined(__GO32__) || defined(__EMX__))
 
1991
 
 
1992
#if (!defined(__DJGPP__) || (__DJGPP__ < 2) || \
 
1993
     ((__DJGPP__ == 2) && (__DJGPP_MINOR__ < 2)))
 
1994
int volatile _doserrno;
 
1995
#endif /* not "djgpp v2.02 or newer" */
 
1996
 
 
1997
#if (!defined(__DJGPP__) || (__DJGPP__ < 2))
 
1998
 
 
1999
unsigned _dos_getcountryinfo(void *countrybuffer)
 
2000
{
 
2001
    asm("movl %0, %%edx": : "g" (countrybuffer));
 
2002
    asm("movl $0x3800, %eax");
 
2003
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2004
    _doserrno = 0;
 
2005
    asm("jnc 1f");
 
2006
    asm("movl %%eax, %0": "=m" (_doserrno));
 
2007
    asm("1:");
 
2008
    return (unsigned)_doserrno;
 
2009
}
 
2010
 
 
2011
unsigned _dos_setftime(int fd, unsigned dosdate, unsigned dostime)
 
2012
{
 
2013
    asm("movl %0, %%ebx": : "g" (fd));
 
2014
    asm("movl %0, %%ecx": : "g" (dostime));
 
2015
    asm("movl %0, %%edx": : "g" (dosdate));
 
2016
    asm("movl $0x5701, %eax");
 
2017
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2018
    _doserrno = 0;
 
2019
    asm("jnc 1f");
 
2020
    asm("movl %%eax, %0": "=m" (_doserrno));
 
2021
    errno = EBADF;
 
2022
    asm("1:");
 
2023
    return (unsigned)_doserrno;
 
2024
}
 
2025
 
 
2026
unsigned _dos_setfileattr(const char *name, unsigned attr)
 
2027
{
 
2028
#if 0   /* stripping of trailing '/' is not needed for unzip-internal use */
 
2029
    unsigned namlen = strlen(name);
 
2030
    char *i_name = alloca(namlen + 1);
 
2031
 
 
2032
    strcpy(i_name, name);
 
2033
    if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':')
 
2034
        i_name[namlen-1] = '\0';
 
2035
    asm("movl %0, %%edx": : "g" (i_name));
 
2036
#else
 
2037
    asm("movl %0, %%edx": : "g" (name));
 
2038
#endif
 
2039
    asm("movl %0, %%ecx": : "g" (attr));
 
2040
    asm("movl $0x4301, %eax");
 
2041
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2042
    _doserrno = 0;
 
2043
    asm("jnc 1f");
 
2044
    asm("movl %%eax, %0": "=m" (_doserrno));
 
2045
    switch (_doserrno) {
 
2046
    case 2:
 
2047
    case 3:
 
2048
           errno = ENOENT;
 
2049
           break;
 
2050
    case 5:
 
2051
           errno = EACCES;
 
2052
           break;
 
2053
    }
 
2054
    asm("1:");
 
2055
    return (unsigned)_doserrno;
 
2056
}
 
2057
 
 
2058
void _dos_getdrive(unsigned *d)
 
2059
{
 
2060
    asm("movl $0x1900, %eax");
 
2061
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2062
    asm("xorb %ah, %ah");
 
2063
    asm("incb %al");
 
2064
    asm("movl %%eax, %0": "=a" (*d));
 
2065
}
 
2066
 
 
2067
unsigned _dos_creat(const char *path, unsigned attr, int *fd)
 
2068
{
 
2069
    asm("movl $0x3c00, %eax");
 
2070
    asm("movl %0, %%edx": :"g" (path));
 
2071
    asm("movl %0, %%ecx": :"g" (attr));
 
2072
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2073
    asm("movl %%eax, %0": "=a" (*fd));
 
2074
    _doserrno = 0;
 
2075
    asm("jnc 1f");
 
2076
    _doserrno = *fd;
 
2077
    switch (_doserrno) {
 
2078
    case 3:
 
2079
           errno = ENOENT;
 
2080
           break;
 
2081
    case 4:
 
2082
           errno = EMFILE;
 
2083
           break;
 
2084
    case 5:
 
2085
           errno = EACCES;
 
2086
           break;
 
2087
    }
 
2088
    asm("1:");
 
2089
    return (unsigned)_doserrno;
 
2090
}
 
2091
 
 
2092
unsigned _dos_close(int fd)
 
2093
{
 
2094
    asm("movl %0, %%ebx": : "g" (fd));
 
2095
    asm("movl $0x3e00, %eax");
 
2096
    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
 
2097
    _doserrno = 0;
 
2098
    asm("jnc 1f");
 
2099
    asm ("movl %%eax, %0": "=m" (_doserrno));
 
2100
    if (_doserrno == 6) {
 
2101
          errno = EBADF;
 
2102
    }
 
2103
    asm("1:");
 
2104
    return (unsigned)_doserrno;
 
2105
}
 
2106
 
 
2107
#endif /* !__DJGPP__ || (__DJGPP__ < 2) */
 
2108
 
 
2109
 
 
2110
static int volumelabel(ZCONST char *name)
 
2111
{
 
2112
    int fd;
 
2113
 
 
2114
    return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
 
2115
}
 
2116
 
 
2117
 
 
2118
#if (defined(__DJGPP__) && (__DJGPP__ >= 2))
 
2119
 
 
2120
#include <dpmi.h>               /* These includes for the country info */
 
2121
#include <go32.h>
 
2122
#include <sys/farptr.h>
 
2123
 
 
2124
/* The above _dos_getcountryinfo function doesn't work with djgpp v2, presumably
 
2125
 * because ds is not set correctly (does it really work at all?). Note that
 
2126
 * this version only sets the date (ie. CountryInfo[0]).
 
2127
 */
 
2128
unsigned _dos_getcountryinfo(void *countrybuffer)
 
2129
{
 
2130
   __dpmi_regs regs;
 
2131
 
 
2132
   regs.x.ax = 0x3800;
 
2133
   regs.x.dx = __tb & 0x0f;
 
2134
   regs.x.ds = (__tb >> 4) & 0xffff;
 
2135
   _doserrno = __dpmi_int(0x21, &regs);
 
2136
 
 
2137
   *(ush*)countrybuffer = _farpeekw(_dos_ds, __tb & 0xfffff);
 
2138
 
 
2139
   return (unsigned)_doserrno;
 
2140
}
 
2141
 
 
2142
 
 
2143
/* Disable determination of "x" bit in st_mode field for [f]stat() calls. */
 
2144
int _is_executable (const char *path, int fhandle, const char *ext)
 
2145
{
 
2146
    return 0;
 
2147
}
 
2148
 
 
2149
#ifndef USE_DJGPP_GLOB
 
2150
/* Prevent globbing of filenames.  This gives the same functionality as
 
2151
 * "stubedit <program> globbing=no" did with DJGPP v1.
 
2152
 */
 
2153
char **__crt0_glob_function(char *_arg)
 
2154
{
 
2155
    return NULL;
 
2156
}
 
2157
#endif /* !USE_DJGPP_GLOB */
 
2158
 
 
2159
#ifndef USE_DJGPP_ENV
 
2160
/* Reduce the size of the executable and remove the functionality to read
 
2161
 * the program's environment from whatever $DJGPP points to.
 
2162
 */
 
2163
void __crt0_load_environment_file(char *_app_name)
 
2164
{
 
2165
}
 
2166
#endif /* !USE_DJGPP_ENV */
 
2167
 
 
2168
#endif /* __DJGPP__ >= 2 */
 
2169
#endif /* __GO32__ || __EMX__ */
 
2170
 
 
2171
 
 
2172
 
 
2173
#ifdef __EMX__
 
2174
#ifdef MORE
 
2175
 
 
2176
/*************************/
 
2177
/* Function screensize() */
 
2178
/*************************/
 
2179
 
 
2180
int screensize(int *tt_rows, int *tt_cols)
 
2181
{
 
2182
    int scr_dimen[2];           /* scr_dimen[0]: columns, src_dimen[1]: rows */
 
2183
 
 
2184
    _scrsize(scr_dimen);
 
2185
    if (tt_rows != NULL) *tt_rows = scr_dimen[1];
 
2186
    if (tt_cols != NULL) *tt_cols = scr_dimen[0];
 
2187
    return 0;
 
2188
}
 
2189
 
 
2190
#endif /* MORE */
 
2191
#endif /* __EMX__ */
 
2192
 
 
2193
 
 
2194
 
 
2195
#ifdef WATCOMC_386
 
2196
#ifdef MORE
 
2197
#include <graph.h>
 
2198
 
 
2199
/*************************/
 
2200
/* Function screensize() */
 
2201
/*************************/
 
2202
 
 
2203
int screensize(int *tt_rows, int *tt_cols)
 
2204
{
 
2205
    struct videoconfig vc;
 
2206
 
 
2207
    _getvideoconfig(&vc);
 
2208
    if (tt_rows != NULL) *tt_rows = (int)(vc.numtextrows);
 
2209
    if (tt_cols != NULL) *tt_cols = (int)(vc.numtextcols);
 
2210
    return 0;
 
2211
}
 
2212
 
 
2213
#endif /* MORE */
 
2214
 
 
2215
 
 
2216
static struct RMINFO {
 
2217
    ulg edi, esi, ebp;
 
2218
    ulg reserved;
 
2219
    ulg ebx, edx, ecx, eax;
 
2220
    ush flags;
 
2221
    ush es,ds,fs,gs;
 
2222
    ush ip_ignored,cs_ignored;
 
2223
    ush sp,ss;
 
2224
};
 
2225
 
 
2226
/* This function is used to call dos interrupts that may not be supported
 
2227
 * by some particular 32-bit DOS extender.  It uses DPMI function 300h to
 
2228
 * simulate a real mode call of the interrupt.  The caller is responsible
 
2229
 * for providing real mode addresses of any buffer areas used.  The docs
 
2230
 * for PMODE/W imply that this should not be necessary for calling the DOS
 
2231
 * interrupts that it doesn't extend, but it crashes when this isn't used. */
 
2232
 
 
2233
static int int86x_realmode(int inter_no, union REGS *in,
 
2234
                           union REGS *out, struct SREGS *seg)
 
2235
{
 
2236
    union REGS local;
 
2237
    struct SREGS localseg;
 
2238
    struct RMINFO rmi;
 
2239
    int r;
 
2240
 
 
2241
    rmi.eax = in->x.eax;
 
2242
    rmi.ebx = in->x.ebx;
 
2243
    rmi.ecx = in->x.ecx;
 
2244
    rmi.edx = in->x.edx;
 
2245
    rmi.edi = in->x.edi;
 
2246
    rmi.esi = in->x.esi;
 
2247
    rmi.ebp = rmi.reserved = 0L;
 
2248
    rmi.es = seg->es;
 
2249
    rmi.ds = seg->ds;
 
2250
    rmi.fs = seg->fs;
 
2251
    rmi.gs = seg->gs;
 
2252
    rmi.sp = rmi.ss = rmi.ip_ignored = rmi.cs_ignored = rmi.flags = 0;
 
2253
    memset(&local, 0, sizeof(local));
 
2254
    memset(&localseg, 0, sizeof(localseg));
 
2255
    local.w.ax = 0x0300;
 
2256
    local.h.bl = inter_no;
 
2257
    local.h.bh = 0;
 
2258
    local.w.cx = 0;
 
2259
    localseg.es = FP_SEG(&rmi);
 
2260
    local.x.edi = FP_OFF(&rmi);
 
2261
    r = int386x(0x31, &local, &local, &localseg);
 
2262
    out->x.eax = rmi.eax;
 
2263
    out->x.ebx = rmi.ebx;
 
2264
    out->x.ecx = rmi.ecx;
 
2265
    out->x.edx = rmi.edx;
 
2266
    out->x.edi = rmi.edi;
 
2267
    out->x.esi = rmi.esi;
 
2268
    out->x.cflag = rmi.flags & INTR_CF;
 
2269
    return r;
 
2270
}
 
2271
 
 
2272
#endif /* WATCOMC_386 */
 
2273
 
 
2274
 
 
2275
 
 
2276
 
 
2277
#ifdef DOS_STAT_BANDAID
 
2278
 
 
2279
/* This papers over a bug in Watcom 10.6's standard library...sigh.
 
2280
 * Apparently it applies to both the DOS and Win32 stat()s. */
 
2281
 
 
2282
int stat_bandaid(const char *path, struct stat *buf)
 
2283
{
 
2284
    char newname[4];
 
2285
 
 
2286
    if (!stat(path, buf))
 
2287
        return 0;
 
2288
    else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
 
2289
        strcpy(newname, path);
 
2290
        newname[strlen(path) - 1] = '\\';   /* stat(".") fails for root! */
 
2291
        return stat(newname, buf);
 
2292
    } else
 
2293
        return -1;
 
2294
}
 
2295
 
 
2296
#endif /* DOS_STAT_BANDAID */
 
2297
 
 
2298
#endif /* !FUNZIP */