2
Copyright (c) 1990-2004 Info-ZIP. All rights reserved.
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
9
/*---------------------------------------------------------------------------
13
Atari-specific routines for use with Info-ZIP's UnZip 5.1 and later.
16
do_wild() <-- generic enough to put in fileio.c?
22
stamp_file() [optional feature]
25
Due to the amazing MiNT library being very, very close to BSD unix's
26
library, I'm using the unix.c as a base for this. Note: If you're not
27
going to compile this with the MiNT libraries (for GNU C, Turbo C, Pure C,
28
Lattice C, or Heat & Serve C), you're going to be in for some nasty work.
29
Most of the modifications in this file were made by Chris Herborth
30
(cherborth@semprini.waterloo-rdp.on.ca) and /should/ be marked with [cjh].
32
---------------------------------------------------------------------------*/
35
#define UNZIP_INTERNAL
37
#include <dirent.h> /* MiNTlibs has dirent [cjh] */
39
static int created_dir; /* used in mapname(), checkdir() */
40
static int renamed_fullpath; /* ditto */
45
/**********************/
46
/* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */
47
/**********************/
49
char *do_wild(__G__ wildspec)
51
ZCONST char *wildspec; /* only used first time on a given dir */
53
static DIR *wild_dir = (DIR *)NULL;
54
static ZCONST char *wildname;
55
static char *dirname, matchname[FILNAMSIZ];
56
static int notfirstcall=FALSE, have_dirname, dirnamelen;
59
/* Even when we're just returning wildspec, we *always* do so in
60
* matchname[]--calling routine is allowed to append four characters
61
* to the returned string, and wildspec may be a pointer to argv[].
63
if (!notfirstcall) { /* first call: must initialize everything */
66
if (!iswild(wildspec)) {
67
strcpy(matchname, wildspec);
73
/* break the wildspec into a directory part and a wildcard filename */
74
if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) {
80
++wildname; /* point at character after '/' */
81
dirnamelen = wildname - wildspec;
82
if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
83
Info(slide, 0x201, ((char *)slide,
84
"warning: cannot allocate wildcard buffers\n"));
85
strcpy(matchname, wildspec);
86
return matchname; /* but maybe filespec was not a wildcard */
88
strncpy(dirname, wildspec, dirnamelen);
89
dirname[dirnamelen] = '\0'; /* terminate for strcpy below */
93
if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
94
while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
95
Trace((stderr, "do_wild: readdir returns %s\n", file->d_name));
96
if (file->d_name[0] == '.' && wildname[0] != '.')
97
continue; /* Unix: '*' and '?' do not match leading dot */
98
/* Need something here for TOS filesystem? [cjh] */
99
if (match(file->d_name, wildname, 0) && /* 0 == case sens. */
100
/* skip "." and ".." directory entries */
101
strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
102
Trace((stderr, "do_wild: match() succeeds\n"));
104
strcpy(matchname, dirname);
105
strcpy(matchname+dirnamelen, file->d_name);
107
strcpy(matchname, file->d_name);
111
/* if we get to here directory is exhausted, so close it */
113
wild_dir = (DIR *)NULL;
116
/* return the raw wildspec in case that works (e.g., directory not
117
* searchable, but filespec was not wild and file is readable) */
118
strcpy(matchname, wildspec);
122
/* last time through, might have failed opendir but returned raw wildspec */
123
if (wild_dir == (DIR *)NULL) {
124
notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
130
/* If we've gotten this far, we've read and matched at least one entry
131
* successfully (in a previous call), so dirname has been copied into
134
while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
135
/* May need special TOS handling here. [cjh] */
136
Trace((stderr, "do_wild: readdir returns %s\n", file->d_name));
137
if (file->d_name[0] == '.' && wildname[0] != '.')
138
continue; /* Unix: '*' and '?' do not match leading dot */
139
if (match(file->d_name, wildname, 0)) { /* 0 == don't ignore case */
140
Trace((stderr, "do_wild: match() succeeds\n"));
142
/* strcpy(matchname, dirname); */
143
strcpy(matchname+dirnamelen, file->d_name);
145
strcpy(matchname, file->d_name);
150
closedir(wild_dir); /* have read at least one entry; nothing left */
151
wild_dir = (DIR *)NULL;
152
notfirstcall = FALSE; /* reset for new wildspec */
157
} /* end function do_wild() */
165
/**********************/
166
/* Function mapattr() */
167
/**********************/
172
ulg tmp = G.crec.external_file_attributes;
174
switch (G.pInfo->hostnum) {
176
tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */
177
G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
181
if ((tmp & 0xF0000000L) != 0x40000000L)
182
tmp &= 0x01FFFFFFL; /* not a dir, mask all ftype bits */
184
tmp &= 0x41FFFFFFL; /* leave directory bit as set */
193
G.pInfo->file_attr = (unsigned)(tmp >> 16);
194
if (G.pInfo->file_attr != 0 || !G.extra_field) {
197
/* Some (non-Info-ZIP) implementations of Zip for Unix and
198
VMS (and probably others ??) leave 0 in the upper 16-bit
199
part of the external_file_attributes field. Instead, they
200
store file permission attributes in some extra field.
201
As a work-around, we search for the presence of one of
202
these extra fields and fall back to the MSDOS compatible
203
part of external_file_attributes if one of the known
204
e.f. types has been detected.
205
Later, we might implement extraction of the permission
206
bits from the VMS extra field. But for now, the work-around
207
should be sufficient to provide "readable" extracted files.
208
(For ASI Unix e.f., an experimental remap of the e.f.
209
mode value IS already provided!)
213
uch *ef = G.extra_field;
214
unsigned ef_len = G.crec.extra_field_length;
217
while (!r && ef_len >= EB_HEADSIZE) {
219
ebLen = (unsigned)makeword(ef+EB_LEN);
220
if (ebLen > (ef_len - EB_HEADSIZE))
221
/* discoverd some e.f. inconsistency! */
225
if (ebLen >= (EB_ASI_MODE+2)) {
227
(unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
228
/* force stop of loop: */
229
ef_len = (ebLen + EB_HEADSIZE);
232
/* else: fall through! */
234
/* "found nondecypherable e.f. with perm. attr" */
239
ef_len -= (ebLen + EB_HEADSIZE);
240
ef += (ebLen + EB_HEADSIZE);
246
/* all remaining cases: expand MSDOS read-only bit into write perms */
248
/* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
249
* Unix attributes in the upper 16 bits of the external attributes
250
* field, just like Info-ZIP's Zip for Unix. We try to use that
251
* value, after a check for consistency with the MSDOS attribute
254
G.pInfo->file_attr = (unsigned)(tmp >> 16);
261
/* Ensure that DOS subdir bit is set when the entry's name ends
262
* in a '/'. Some third-party Zip programs fail to set the subdir
263
* bit for directory entries.
265
if ((tmp & 0x10) == 0) {
266
extent fnlen = strlen(G.filename);
267
if (fnlen > 0 && G.filename[fnlen-1] == '/')
270
/* read-only bit --> write perms; subdir bit --> dir exec bit */
271
tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4;
272
if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
273
/* keep previous G.pInfo->file_attr setting, when its "owner"
274
* part appears to be consistent with DOS attribute flags!
277
G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
279
} /* end switch (host-OS-created-by) */
281
/* for originating systems with no concept of "group," "other," "system": */
282
umask( (int)(tmp=umask(0)) ); /* apply mask to expanded r/w(/x) perms */
283
G.pInfo->file_attr &= ~tmp;
287
} /* end function mapattr() */
293
/************************/
294
/* Function mapname() */
295
/************************/
297
int mapname(__G__ renamed)
302
* MPN_OK - no problem detected
303
* MPN_INF_TRUNC - caution (truncated filename)
304
* MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
305
* MPN_ERR_SKIP - error -> skip entry
306
* MPN_ERR_TOOLONG - error -> path is too long
307
* MPN_NOMEM - error (memory allocation failed) -> skip entry
308
* [also MPN_VOL_LABEL, MPN_CREATED_DIR]
311
char pathcomp[FILNAMSIZ]; /* path-component buffer */
312
char *pp, *cp=(char *)NULL; /* character pointers */
313
char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
314
#ifdef ACORN_FTYPE_NFS
315
char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */
316
RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */
318
int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */
320
register unsigned workch; /* hold the character being tested */
323
/*---------------------------------------------------------------------------
324
Initialize various pointers and counters and stuff.
325
---------------------------------------------------------------------------*/
327
if (G.pInfo->vollabel)
328
return MPN_VOL_LABEL; /* can't set disk volume labels on Atari */
330
/* can create path as long as not just freshening, or if user told us */
331
G.create_dirs = (!uO.fflag || renamed);
333
created_dir = FALSE; /* not yet */
335
/* user gave full pathname: don't prepend rootpath */
336
renamed_fullpath = (renamed && (*G.filename == '/'));
338
if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
339
return MPN_NOMEM; /* initialize path buffer, unless no memory */
341
*pathcomp = '\0'; /* initialize translation buffer */
342
pp = pathcomp; /* point to translation buffer */
343
if (uO.jflag) /* junking directories */
344
cp = (char *)strrchr(G.filename, '/');
345
if (cp == (char *)NULL) /* no '/' or not junking dirs */
346
cp = G.filename; /* point to internal zipfile-member pathname */
348
++cp; /* point to start of last component of path */
350
/*---------------------------------------------------------------------------
351
Begin main loop through characters in filename.
352
---------------------------------------------------------------------------*/
354
while ((workch = (uch)*cp++) != 0) {
357
case '/': /* can assume -j flag not given */
359
if (strcmp(pathcomp, ".") == 0) {
360
/* don't bother appending "./" to the path */
362
} else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
363
/* "../" dir traversal detected, skip over it */
365
killed_ddot = TRUE; /* set "show message" flag */
367
/* when path component is not empty, append it now */
368
if (*pathcomp != '\0' &&
369
((error = checkdir(__G__ pathcomp, APPEND_DIR))
370
& MPN_MASK) > MPN_INF_TRUNC)
372
pp = pathcomp; /* reset conversion buffer for next piece */
373
lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
376
case ';': /* VMS version (or DEC-20 attrib?) */
378
*pp++ = ';'; /* keep for now; remove VMS ";##" */
379
break; /* later, if requested */
381
#ifdef ACORN_FTYPE_NFS
382
case ',': /* NFS filetype extension */
384
*pp++ = ','; /* keep for now; may need to remove */
385
break; /* later, if requested */
389
case ' ': /* change spaces to underscore under */
390
*pp++ = '_'; /* MTS; leave as spaces under Unix */
395
/* allow European characters in filenames: */
396
if (isprint(workch) || (128 <= workch && workch <= 254))
397
*pp++ = (char)workch;
400
} /* end while loop */
402
/* Show warning when stripping insecure "parent dir" path components */
403
if (killed_ddot && QCOND2) {
404
Info(slide, 0, ((char *)slide,
405
"warning: skipped \"../\" path component(s) in %s\n",
406
FnFilter1(G.filename)));
407
if (!(error & ~MPN_MASK))
408
error = (error & MPN_MASK) | PK_WARN;
411
/*---------------------------------------------------------------------------
412
Report if directory was created (and no file to create: filename ended
413
in '/'), check name to be sure it exists, and combine path and name be-
415
---------------------------------------------------------------------------*/
417
if (G.filename[strlen(G.filename) - 1] == '/') {
418
checkdir(__G__ G.filename, GETPATH);
421
Info(slide, 0, ((char *)slide, " creating: %s\n",
422
FnFilter1(G.filename)));
424
/* set dir time (note trailing '/') */
425
return (error & ~MPN_MASK) | MPN_CREATED_DIR;
427
/* dir existed already; don't look for data to extract */
428
return (error & ~MPN_MASK) | MPN_INF_SKIP;
431
*pp = '\0'; /* done with pathcomp: terminate it */
433
/* if not saving them, remove VMS version numbers (appended ";###") */
434
if (!uO.V_flag && lastsemi) {
436
while (isdigit((uch)(*pp)))
438
if (*pp == '\0') /* only digits between ';' and end: nuke */
442
#ifdef ACORN_FTYPE_NFS
443
/* translate Acorn filetype information if asked to do so */
444
if (uO.acorn_nfs_ext &&
445
(ef_spark = (RO_extra_block *)
446
getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
447
!= (RO_extra_block *)NULL)
449
/* file *must* have a RISC OS extra field */
450
long ft = (long)makelong((ef_spark->loadaddr);
454
while (isxdigit((uch)(*pp))) ++pp;
455
if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
457
if ((ft & 1<<31)==0) ft=0x000FFD00;
458
sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
460
#endif /* ACORN_FTYPE_NFS */
462
if (*pathcomp == '\0') {
463
Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n",
464
FnFilter1(G.filename)));
465
return (error & ~MPN_MASK) | MPN_ERR_SKIP;
468
checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */
469
checkdir(__G__ G.filename, GETPATH);
473
} /* end function mapname() */
478
#if 0 /*========== NOTES ==========*/
480
extract-to dir: a:path/
481
buildpath: path1/path2/ ... (NULL-terminated)
485
loop over chars in zipfile member name
486
checkdir(path component, COMPONENT | CREATEDIR) --> map as required?
487
(d:/tmp/unzip/) (disk:[tmp.unzip.)
488
(d:/tmp/unzip/jj/) (disk:[tmp.unzip.jj.)
489
(d:/tmp/unzip/jj/temp/) (disk:[tmp.unzip.jj.temp.)
490
finally add filename itself and check for existence? (could use with rename)
491
(d:/tmp/unzip/jj/temp/msg.outdir) (disk:[tmp.unzip.jj.temp]msg.outdir)
492
checkdir(name, GETPATH) --> copy path to name and free space
499
/***********************/
500
/* Function checkdir() */
501
/***********************/
503
int checkdir(__G__ pathcomp, flag)
509
* MPN_OK - no problem detected
510
* MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
511
* MPN_INF_SKIP - path doesn't exist, not allowed to create
512
* MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
513
* exists and is not a directory, but is supposed to be
514
* MPN_ERR_TOOLONG - path is too long
515
* MPN_NOMEM - can't allocate memory for filename buffers
518
static int rootlen = 0; /* length of rootpath */
519
static char *rootpath; /* user's "extract-to" directory */
520
static char *buildpath; /* full path (so far) to extracted file */
521
static char *end; /* pointer to end of buildpath ('\0') */
524
# define FUNCTION (flag & FN_MASK)
527
/*---------------------------------------------------------------------------
528
APPEND_DIR: append the path component to the path being built and check
529
for its existence. If doesn't exist and we are creating directories, do
530
so for this one; else signal success or error as appropriate.
531
---------------------------------------------------------------------------*/
533
if (FUNCTION == APPEND_DIR) {
534
int too_long = FALSE;
535
/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */
540
Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
541
while ((*end = *pathcomp++) != '\0')
543
/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */
544
#ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */
545
if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */
546
*(end = old_end + FILENAME_MAX) = '\0';
549
/* GRR: could do better check, see if overrunning buffer as we go:
550
* check end-buildpath after each append, set warning variable if
551
* within 20 of FILNAMSIZ; then if var set, do careful check when
552
* appending. Clear variable when begin new path. */
554
if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */
555
too_long = TRUE; /* check if extracting directory? */
556
if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */
557
if (!G.create_dirs) { /* told not to create (freshening) */
559
return MPN_INF_SKIP; /* path doesn't exist: nothing to do */
562
Info(slide, 1, ((char *)slide,
563
"checkdir error: path too long: %s\n",
564
FnFilter1(buildpath)));
566
/* no room for filenames: fatal */
567
return MPN_ERR_TOOLONG;
569
if (mkdir(buildpath, 0777) == -1) { /* create the directory */
570
Info(slide, 1, ((char *)slide,
571
"checkdir error: cannot create %s\n\
572
unable to process %s.\n",
573
FnFilter2(buildpath), FnFilter1(G.filename)));
575
/* path didn't exist, tried to create, failed */
579
} else if (!S_ISDIR(G.statbuf.st_mode)) {
580
Info(slide, 1, ((char *)slide,
581
"checkdir error: %s exists but is not directory\n\
582
unable to process %s.\n",
583
FnFilter2(buildpath), FnFilter(G.filename)));
585
/* path existed but wasn't dir */
589
Info(slide, 1, ((char *)slide,
590
"checkdir error: path too long: %s\n", FnFilter1(buildpath)));
592
/* no room for filenames: fatal */
593
return MPN_ERR_TOOLONG;
597
Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
600
} /* end if (FUNCTION == APPEND_DIR) */
602
/*---------------------------------------------------------------------------
603
GETPATH: copy full path to the string pointed at by pathcomp, and free
605
---------------------------------------------------------------------------*/
607
if (FUNCTION == GETPATH) {
608
strcpy(pathcomp, buildpath);
609
Trace((stderr, "getting and freeing path [%s]\n",
610
FnFilter1(pathcomp)));
612
buildpath = end = (char *)NULL;
616
/*---------------------------------------------------------------------------
617
APPEND_NAME: assume the path component is the filename; append it and
618
return without checking for existence.
619
---------------------------------------------------------------------------*/
621
if (FUNCTION == APPEND_NAME) {
622
/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */
627
Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
628
while ((*end = *pathcomp++) != '\0') {
630
/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */
631
#ifdef SHORT_NAMES /* truncate name at 14 characters, typically */
632
if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */
633
*(end = old_end + FILENAME_MAX) = '\0';
635
if ((end-buildpath) >= FILNAMSIZ) {
637
Info(slide, 0x201, ((char *)slide,
638
"checkdir warning: path too long; truncating\n\
640
FnFilter1(G.filename), FnFilter2(buildpath)));
641
return MPN_INF_TRUNC; /* filename truncated */
644
Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
645
/* could check for existence here, prompt for new name... */
649
/*---------------------------------------------------------------------------
650
INIT: allocate and initialize buffer space for the file currently being
651
extracted. If file was renamed with an absolute path, don't prepend the
653
---------------------------------------------------------------------------*/
655
/* GRR: for VMS and TOPS-20, add up to 13 to strlen */
657
if (FUNCTION == INIT) {
658
Trace((stderr, "initializing buildpath to "));
659
#ifdef ACORN_FTYPE_NFS
660
if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+
661
(uO.acorn_nfs_ext ? 5 : 1)))
663
if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
667
if ((rootlen > 0) && !renamed_fullpath) {
668
strcpy(buildpath, rootpath);
669
end = buildpath + rootlen;
674
Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
678
/*---------------------------------------------------------------------------
679
ROOT: if appropriate, store the path in rootpath and create it if
680
necessary; else assume it's a zipfile member and return. This path
681
segment gets used in extracting all members from every zipfile specified
683
---------------------------------------------------------------------------*/
685
#if (!defined(SFX) || defined(SFX_EXDIR))
686
if (FUNCTION == ROOT) {
687
Trace((stderr, "initializing root path to [%s]\n", pathcomp));
688
if (pathcomp == (char *)NULL) {
692
if (rootlen > 0) /* rootpath was already set, nothing to do */
694
if ((rootlen = strlen(pathcomp)) > 0) {
697
if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
701
strcpy(tmproot, pathcomp);
702
if (tmproot[rootlen-1] == '/') {
703
tmproot[--rootlen] = '\0';
705
if (rootlen > 0 && (stat(tmproot, &G.statbuf) ||
706
!S_ISDIR(G.statbuf.st_mode)))
707
{ /* path does not exist */
708
if (!G.create_dirs /* || iswild(tmproot) */ ) {
711
/* skip (or treat as stored file) */
714
/* create the directory (could add loop here scanning tmproot
715
* to create more than one level, but why really necessary?) */
716
if (mkdir(tmproot, 0777) == -1) {
717
Info(slide, 1, ((char *)slide,
718
"checkdir: cannot create extraction directory: %s\n",
719
FnFilter1(tmproot)));
722
/* path didn't exist, tried to create, and failed: */
723
/* file exists, or 2+ subdir levels required */
727
tmproot[rootlen++] = '/';
728
tmproot[rootlen] = '\0';
729
if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
734
Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
738
#endif /* !SFX || SFX_EXDIR */
740
/*---------------------------------------------------------------------------
741
END: free rootpath, immediately prior to program exit.
742
---------------------------------------------------------------------------*/
744
if (FUNCTION == END) {
745
Trace((stderr, "freeing rootpath\n"));
753
return MPN_INVALID; /* should never reach */
755
} /* end function checkdir() */
761
/****************************/
762
/* Function close_outfile() */
763
/****************************/
765
void close_outfile(__G) /* GRR: change to return PK-style warning level */
768
#ifdef USE_EF_UT_TIME
769
unsigned eb_izux_flg;
776
/*---------------------------------------------------------------------------
777
If symbolic links are supported, allocate storage for a symlink control
778
structure, put the uncompressed "data" and other required info in it, and
779
add the structure to the "deferred symlinks" chain. Since we know it's a
780
symbolic link to start with, we shouldn't have to worry about overflowing
781
unsigned ints with unsigned longs.
782
---------------------------------------------------------------------------*/
784
/* symlinks allowed on minix filesystems [cjh]
785
* Hopefully this will work properly... We won't bother to try if
786
* MiNT isn't present; the symlink should fail if we're on a TOS
788
* BUG: should we copy the original file to the "symlink" if the
792
unsigned ucsize = (unsigned)G.lrec.ucsize;
793
extent slnk_entrysize = sizeof(slinkentry) + ucsize +
795
slinkentry *slnk_entry;
797
if ((unsigned)slnk_entrysize < ucsize) {
798
Info(slide, 0x201, ((char *)slide,
799
"warning: symbolic link (%s) failed: mem alloc overflow\n",
800
FnFilter1(G.filename)));
804
if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
805
Info(slide, 0x201, ((char *)slide,
806
"warning: symbolic link (%s) failed: no mem\n",
807
FnFilter1(G.filename)));
810
slnk_entry->next = NULL;
811
slnk_entry->targetlen = ucsize;
812
slnk_entry->attriblen = 0; /* don't set attributes for symlinks */
813
slnk_entry->target = slnk_entry->buf;
814
slnk_entry->fname = slnk_entry->target + ucsize + 1;
815
strcpy(slnk_entry->fname, G.filename);
817
/* reopen the "link data" file for reading */
818
G.outfile = fopen(G.filename, FOPR);
821
fread(slnk_entry->target, 1, ucsize, G.outfile) != (int)ucsize)
823
Info(slide, 0x201, ((char *)slide,
824
"warning: symbolic link (%s) failed\n",
825
FnFilter1(G.filename)));
830
fclose(G.outfile); /* close "link" file for good... */
831
slnk_entry->target[ucsize] = '\0';
833
Info(slide, 0, ((char *)slide, "-> %s ",
834
FnFilter1(slnk_entry->target)));
835
/* add this symlink record to the list of deferred symlinks */
836
if (G.slink_last != NULL)
837
G.slink_last->next = slnk_entry;
839
G.slink_head = slnk_entry;
840
G.slink_last = slnk_entry;
844
/*---------------------------------------------------------------------------
845
Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
846
time: adjust base year from 1980 to 1970, do usual conversions from
847
yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
848
light savings time differences.
849
---------------------------------------------------------------------------*/
851
#ifdef USE_EF_UT_TIME
852
eb_izux_flg = (G.extra_field
856
? ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length,
857
0, G.lrec.last_mod_dos_datetime, &zt, NULL)
859
if (eb_izux_flg & EB_UT_FL_MTIME) {
860
tp.modtime = zt.mtime;
861
TTrace((stderr, "\nclose_outfile: Unix e.f. modif. time = %ld\n",
864
tp.modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
866
if (eb_izux_flg & EB_UT_FL_ATIME) {
867
tp.actime = zt.atime;
868
TTrace((stderr, "close_outfile: Unix e.f. access time = %ld\n",
871
tp.actime = tp.modtime;
872
TTrace((stderr, "\nclose_outfile: modification/access times = %ld\n",
875
#else /* !USE_EF_UT_TIME */
876
tp.actime = tp.modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
878
TTrace((stderr, "\nclose_outfile: modification/access times = %ld\n",
880
#endif /* ?USE_EF_UT_TIME */
882
/* set the file's access and modification times */
883
if (utime(G.filename, &tp))
884
Info(slide, 0x201, ((char *)slide,
885
"warning: cannot set the time for %s\n", FnFilter1(G.filename)));
887
/*---------------------------------------------------------------------------
888
Change the file permissions from default ones to those stored in the
890
---------------------------------------------------------------------------*/
893
if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
894
perror("chmod (file attributes) error");
897
} /* end function close_outfile() */
904
/***************************/
905
/* Function stamp_file() */
906
/***************************/
908
int stamp_file(fname, modtime)
914
tp.modtime = tp.actime = modtime;
915
return (utime(fname, &tp));
917
} /* end function stamp_file() */
919
#endif /* TIMESTAMP */
926
/************************/
927
/* Function version() */
928
/************************/
937
sprintf((char *)slide, LoadFarString(CompiledWith),
943
"cc ", (sprintf(buf, " version %d", _RELEASE), buf),
946
"Turbo C", (sprintf(buf, " (0x%04x = %d)", __TURBOC__, __TURBOC__), buf),
948
"unknown compiler", "",
959
" (Atari ST/TT/Falcon030)",
968
(*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
970
} /* end function version() */