/*---------------------------------------------------------------------------

  unix.c

  Unix-specific routines for use with Info-ZIP's UnZip 5.1 and later.

  ---------------------------------------------------------------------------*/


#include "unzip.h"


/**********************/
/* Function mapattr() */
/**********************/

int mapattr()
{
    ulg  tmp = crec.external_file_attributes;

    switch (pInfo->hostnum) {
        case UNIX_:
        case VMS_:
            pInfo->file_attr = (unsigned)(tmp >> 16);
            return 0;
        case AMIGA_:
            tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
            pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
            break;
        /* all remaining cases:  expand MSDOS read-only bit into write perms */
        case FS_FAT_:
        case FS_HPFS_:
        case FS_NTFS_:
        case MAC_:
        case ATARI_:             /* (used to set = 0666) */
        case TOPS20_:
        default:
            tmp = !(tmp & 1) << 1;   /* read-only bit --> write perms bits */
            pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
            break;
    } /* end switch (host-OS-created-by) */

    /* for originating systems with no concept of "group," "other," "system": */
    umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
    pInfo->file_attr &= ~tmp;

    return 0;

} /* end function mapattr() */





/**************************************/
/* Function set_file_time_and_close() */
/**************************************/

void set_file_time_and_close()
{
    static short yday[]={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
    long m_time;
    int yr, mo, dy, hh, mm, ss, leap, days=0;
    struct utimbuf tp;
#   define YRBASE  1970
#ifndef __386BSD__
#ifdef BSD
    static struct timeb tbp;
#else /* !BSD */
    extern long timezone;
#endif /* ?BSD */
#endif /* !__386BSD__ */


/*---------------------------------------------------------------------------
    If symbolic links are supported, allocate a storage area, put the uncom-
    pressed "data" in it, and create the link.  Since we know it's a symbolic
    link to start with, we shouldn't have to worry about overflowing unsigned
    ints with unsigned longs.
  ---------------------------------------------------------------------------*/

#ifdef SYMLINKS
    if (symlnk) {
        unsigned ucsize = (unsigned)lrec.ucsize;
        char *linktarget = (char *)malloc((unsigned)lrec.ucsize+1);

        close(outfd);                       /* close "data" file... */
        outfd = open(filename, O_RDONLY);   /* ...and reopen for reading */
        if (!linktarget || (read(outfd, linktarget, ucsize) != ucsize)) {
            fprintf(stderr, "\nwarning:  symbolic link (%s) failed\n",
              filename);
            if (linktarget)
                free(linktarget);
            close(outfd);
            return;
        }
        close(outfd);                       /* close "data" file for good... */
        unlink(filename);                   /* ...and delete it */
        linktarget[ucsize] = '\0';
        fprintf(stdout, "-> %s ", linktarget);
        if (symlink(linktarget, filename))  /* create the real link */
            perror("symlink error");
        free(linktarget);
        return;                             /* can't set time on symlinks */
    }
#endif /* SYMLINKS */

    close(outfd);
    if (cflag)          /* can't set time on stdout */
        return;

/*---------------------------------------------------------------------------
    Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
    time:  adjust base year from 1980 to 1970, do usual conversions from
    yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
    light savings time differences.
  ---------------------------------------------------------------------------*/

    yr = ((lrec.last_mod_file_date >> 9) & 0x7f) + (1980 - YRBASE);
    mo = ((lrec.last_mod_file_date >> 5) & 0x0f) - 1;
    dy = (lrec.last_mod_file_date & 0x1f) - 1;
    hh = (lrec.last_mod_file_time >> 11) & 0x1f;
    mm = (lrec.last_mod_file_time >> 5) & 0x3f;
    ss = (lrec.last_mod_file_time & 0x1f) * 2;

    /* leap = # of leap yrs from YRBASE up to but not including current year */
    leap = ((yr + YRBASE - 1) / 4);   /* leap year base factor */

    /* how many days from YRBASE to this year? (& add expired days this year) */
    days = (yr * 365) + (leap - 492) + yday[mo];

    /* if year is a leap year and month is after February, add another day */
    if ((mo > 1) && ((yr+YRBASE)%4 == 0) && ((yr+YRBASE) != 2100))
        ++days;   /* OK through 2199 */

    /* convert date & time to seconds relative to 00:00:00, 01/01/YRBASE */
    m_time = ((days + dy) * 86400) + (hh * 3600) + (mm * 60) + ss;

    /* adjust for local timezone */
#ifdef BSD
#ifdef __386BSD__
    m_time -= localtime(&m_time)->tm_gmtoff;  /* seconds EAST of GMT:  subtr. */
#else /* !__386BSD__ */
    ftime(&tbp);                    /* get `timezone' */
    m_time += tbp.timezone * 60L;   /* seconds WEST of GMT:  add */
#endif /* ?__386BSD__ */
#else /* !BSD */
    tzset();              /* get `timezone' */
    m_time += timezone;   /* seconds WEST of GMT:  add */
#endif /* ?BSD */

    /* adjust for daylight savings time (or local equivalent) */
#ifndef __386BSD__  /* (DST already added to tm_gmtoff, so skip tm_isdst) */
    if (localtime(&m_time)->tm_isdst)
        m_time -= 60L * 60L;   /* adjust for daylight savings time */
#endif

    /* set the file's access and modification times */
    tp.actime = tp.modtime = m_time;
    if (utime(filename, &tp))
        fprintf(stderr, "warning:  can't set the time for %s\n", filename);

} /* end function set_file_time_and_close() */
