1/*
2  Copyright (c) 1990-1999 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 1999-Oct-05 or later
5  (the contents of which are also included in zip.h) for terms of use.
6  If, for some reason, both of these files are missing, the Info-ZIP license
7  also may be found at:  ftp://ftp.cdrom.com/pub/infozip/license.html
8
9  This AtheOS - specific file is based on unix.c and beos.c;
10  changes by Ruslan Nickolaev (nruslan@hotbox.ru)
11*/
12
13#include "zip.h"
14
15#ifndef UTIL    /* the companion #endif is a bit of ways down ... */
16
17#include <time.h>
18#include <dirent.h>
19#include <sys/types.h>
20#include <sys/errno.h>
21#include <limits.h>
22#include <sys/stat.h>
23#include <sys/fcntl.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <atheos/fs_attribs.h>
28
29
30#define PAD 0
31#define PATH_END '/'
32
33/* Library functions not in (most) header files */
34
35#ifdef _POSIX_VERSION
36#  include <utime.h>
37#else
38   int utime OF((char *, time_t *));
39#endif
40
41extern char *label;
42local ulg label_time = 0;
43local ulg label_mode = 0;
44local time_t label_utim = 0;
45
46/* Local functions */
47local char *readd OF((DIR *));
48local int get_attr_dir( const char *, char **, off_t * );
49local int add_UT_ef( struct zlist far * );
50local int add_Ux_ef( struct zlist far * );
51local int add_At_ef( struct zlist far * );
52
53local char *readd(d)
54DIR *d;                 /* directory stream to read from */
55/* Return a pointer to the next name in the directory stream d, or NULL if
56   no more entries or an error occurs. */
57{
58  struct dirent *e;
59
60  e = readdir(d);
61  return e == NULL ? (char *) NULL : e->d_name;
62}
63
64int procname(n, caseflag)
65char *n;                /* name to process */
66int caseflag;           /* true to force case-sensitive match */
67/* Process a name or sh expression to operate on (or exclude).  Return
68   an error code in the ZE_ class. */
69{
70  char *a;              /* path and name for recursion */
71  DIR *d;               /* directory stream from opendir() */
72  char *e;              /* pointer to name from readd() */
73  int m;                /* matched flag */
74  char *p;              /* path for recursion */
75  struct stat s;        /* result of stat() */
76  struct zlist far *z;  /* steps through zfiles list */
77
78  if (strcmp(n, "-") == 0)   /* if compressing stdin */
79    return newname(n, 0, caseflag);
80  else if (LSSTAT(n, &s))
81  {
82    /* Not a file or directory--search for shell expression in zip file */
83    p = ex2in(n, 0, (int *)NULL);       /* shouldn't affect matching chars */
84    m = 1;
85    for (z = zfiles; z != NULL; z = z->nxt) {
86      if (MATCH(p, z->iname, caseflag))
87      {
88        z->mark = pcount ? filter(z->zname, caseflag) : 1;
89        if (verbose)
90            fprintf(mesg, "zip diagnostic: %scluding %s\n",
91               z->mark ? "in" : "ex", z->name);
92        m = 0;
93      }
94    }
95    free((zvoid *)p);
96    return m ? ZE_MISS : ZE_OK;
97  }
98
99  /* Live name--use if file, recurse if directory */
100  if ((s.st_mode & S_IFREG) == S_IFREG ||
101      (s.st_mode & S_IFLNK) == S_IFLNK)
102  {
103    /* add or remove name of file */
104    if ((m = newname(n, 0, caseflag)) != ZE_OK)
105      return m;
106  }
107  else if ((s.st_mode & S_IFDIR) == S_IFDIR)
108  {
109    /* Add trailing / to the directory name */
110    if ((p = malloc(strlen(n)+2)) == NULL)
111      return ZE_MEM;
112    if (strcmp(n, ".") == 0) {
113      *p = '\0';  /* avoid "./" prefix and do not create zip entry */
114    } else {
115      strcpy(p, n);
116      a = p + strlen(p);
117      if (a[-1] != '/')
118        strcpy(a, "/");
119      if (dirnames && (m = newname(p, 1, caseflag)) != ZE_OK) {
120        free((zvoid *)p);
121        return m;
122      }
123    }
124    /* recurse into directory */
125    if (recurse && (d = opendir(n)) != NULL)
126    {
127      while ((e = readd(d)) != NULL) {
128        if (strcmp(e, ".") && strcmp(e, ".."))
129        {
130          if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
131          {
132            closedir(d);
133            free((zvoid *)p);
134            return ZE_MEM;
135          }
136          strcat(strcpy(a, p), e);
137          if ((m = procname(a, caseflag)) != ZE_OK)   /* recurse on name */
138          {
139            if (m == ZE_MISS)
140              zipwarn("name not matched: ", a);
141            else
142              ziperr(m, a);
143          }
144          free((zvoid *)a);
145        }
146      }
147      closedir(d);
148    }
149    free((zvoid *)p);
150  } /* (s.st_mode & S_IFDIR) */
151  else
152    zipwarn("ignoring special file: ", n);
153  return ZE_OK;
154}
155
156char *ex2in(x, isdir, pdosflag)
157char *x;                /* external file name */
158int isdir;              /* input: x is a directory */
159int *pdosflag;          /* output: force MSDOS file attributes? */
160/* Convert the external file name to a zip file name, returning the malloc'ed
161   string or NULL if not enough memory. */
162{
163  char *n;              /* internal file name (malloc'ed) */
164  char *t = NULL;       /* shortened name */
165  int dosflag;
166
167  dosflag = dosify;  /* default for non-DOS and non-OS/2 */
168
169  /* Find starting point in name before doing malloc */
170  /* Strip "//host/share/" part of a UNC name */
171  if (!strncmp(x,"//",2) && (x[2] != '\0' && x[2] != '/')) {
172    n = x + 2;
173    while (*n != '\0' && *n != '/')
174      n++;              /* strip host name */
175    if (*n != '\0') {
176      n++;
177      while (*n != '\0' && *n != '/')
178        n++;            /* strip `share' name */
179    }
180    if (*n != '\0')
181      t = n + 1;
182  } else
183      t = x;
184  while (*t == '/')
185    t++;                /* strip leading '/' chars to get a relative path */
186  while (*t == '.' && t[1] == '/')
187    t += 2;             /* strip redundant leading "./" sections */
188
189  /* Make changes, if any, to the copied name (leave original intact) */
190  if (!pathput)
191    t = last(t, PATH_END);
192
193  /* Malloc space for internal name and copy it */
194  if ((n = malloc(strlen(t) + 1)) == NULL)
195    return NULL;
196  strcpy(n, t);
197
198  if (isdir == 42) return n;    /* avoid warning on unused variable */
199
200  if (dosify)
201    msname(n);
202
203  /* Returned malloc'ed name */
204  if (pdosflag)
205    *pdosflag = dosflag;
206  return n;
207}
208
209char *in2ex(n)
210char *n;                /* internal file name */
211/* Convert the zip file name to an external file name, returning the malloc'ed
212   string or NULL if not enough memory. */
213{
214  char *x;              /* external file name */
215
216  if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
217    return NULL;
218  strcpy(x, n);
219  return x;
220}
221
222/*
223 * XXX use ztimbuf in both POSIX and non POSIX cases ?
224 */
225void stamp(f, d)
226char *f;                /* name of file to change */
227ulg d;                  /* dos-style time to change it to */
228/* Set last updated and accessed time of file f to the DOS time d. */
229{
230#ifdef _POSIX_VERSION
231  struct utimbuf u;     /* argument for utime()  const ?? */
232#else
233  time_t u[2];          /* argument for utime() */
234#endif
235
236  /* Convert DOS time to time_t format in u */
237#ifdef _POSIX_VERSION
238  u.actime = u.modtime = dos2unixtime(d);
239  utime(f, &u);
240#else
241  u[0] = u[1] = dos2unixtime(d);
242  utime(f, u);
243#endif
244
245}
246
247ulg filetime(f, a, n, t)
248char *f;                /* name of file to get info on */
249ulg *a;                 /* return value: file attributes */
250long *n;                /* return value: file size */
251iztimes *t;             /* return value: access, modific. and creation times */
252/* If file *f does not exist, return 0.  Else, return the file's last
253   modified date and time as an MSDOS date and time.  The date and
254   time is returned in a long with the date most significant to allow
255   unsigned integer comparison of absolute times.  Also, if a is not
256   a NULL pointer, store the file attributes there, with the high two
257   bytes being the Unix attributes, and the low byte being a mapping
258   of that to DOS attributes.  If n is not NULL, store the file size
259   there.  If t is not NULL, the file's access, modification and creation
260   times are stored there as UNIX time_t values.
261   If f is "-", use standard input as the file. If f is a device, return
262   a file size of -1 */
263{
264  struct stat s;        /* results of stat() */
265  char *name;
266  int len = strlen(f);
267
268  if (f == label) {
269    if (a != NULL)
270      *a = label_mode;
271    if (n != NULL)
272      *n = -2L; /* convention for a label name */
273    if (t != NULL)
274      t->atime = t->mtime = t->ctime = label_utim;
275    return label_time;
276  }
277  if ((name = malloc(len + 1)) == NULL {
278    ZIPERR(ZE_MEM, "filetime");
279  }
280  strcpy(name, f);
281  if (name[len - 1] == '/')
282    name[len - 1] = '\0';
283  /* not all systems allow stat'ing a file with / appended */
284  if (strcmp(f, "-") == 0) {
285    if (fstat(fileno(stdin), &s) != 0) {
286      free(name);
287      error("fstat(stdin)");
288    }
289  }
290  else if (LSSTAT(name, &s) != 0) {
291    /* Accept about any file kind including directories
292     * (stored with trailing / with -r option)
293     */
294    free(name);
295    return 0;
296  }
297  free(name);
298
299  if (a != NULL) {
300#ifndef OS390
301    *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
302#else
303/*
304**  The following defines are copied from the unizip source and represent the
305**  legacy Unix mode flags.  These fixed bit masks are no longer required
306**  by XOPEN standards - the S_IS### macros being the new recommended method.
307**  The approach here of setting the legacy flags by testing the macros should
308**  work under any _XOPEN_SOURCE environment (and will just rebuild the same bit
309**  mask), but is required if the legacy bit flags differ from legacy Unix.
310*/
311#define UNX_IFDIR      0040000     /* Unix directory */
312#define UNX_IFREG      0100000     /* Unix regular file */
313#define UNX_IFSOCK     0140000     /* Unix socket (BSD, not SysV or Amiga) */
314#define UNX_IFLNK      0120000     /* Unix symbolic link (not SysV, Amiga) */
315#define UNX_IFBLK      0060000     /* Unix block special       (not Amiga) */
316#define UNX_IFCHR      0020000     /* Unix character special   (not Amiga) */
317#define UNX_IFIFO      0010000     /* Unix fifo    (BCC, not MSC or Amiga) */
318    {
319    mode_t legacy_modes;
320
321    /* Initialize with permission bits - which are not implementation optional */
322    legacy_modes = s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
323    if (S_ISDIR(s.st_mode))
324      legacy_modes |= UNX_IFDIR;
325    if (S_ISREG(s.st_mode))
326      legacy_modes |= UNX_IFREG;
327    if (S_ISLNK(s.st_mode))
328      legacy_modes |= UNX_IFLNK;
329    if (S_ISBLK(s.st_mode))
330      legacy_modes |= UNX_IFBLK;
331    if (S_ISCHR(s.st_mode))
332      legacy_modes |= UNX_IFCHR;
333    if (S_ISFIFO(s.st_mode))
334      legacy_modes |= UNX_IFIFO;
335    if (S_ISSOCK(s.st_mode))
336      legacy_modes |= UNX_IFSOCK;
337    *a = ((ulg)legacy_modes << 16) | !(s.st_mode & S_IWRITE);
338    }
339#endif
340    if ((s.st_mode & S_IFMT) == S_IFDIR) {
341      *a |= MSDOS_DIR_ATTR;
342    }
343  }
344  if (n != NULL)
345    *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;
346  if (t != NULL) {
347    t->atime = s.st_atime;
348    t->mtime = s.st_mtime;
349    t->ctime = t->mtime;   /* best guess, (s.st_ctime: last status change!!) */
350  }
351  return unix2dostime(&s.st_mtime);
352}
353
354/* ----------------------------------------------------------------------
355
356Return a malloc()'d buffer containing all of the attributes and their names
357for the file specified in name.  You have to free() this yourself.  The length
358of the buffer is also returned.
359
360If get_attr_dir() fails, the buffer will be NULL, total_size will be 0,
361and an error will be returned:
362
363    EOK    - no errors occurred
364    EINVAL - attr_buff was pointing at a buffer
365    ENOMEM - insufficient memory for attribute buffer
366
367Other errors are possible (whatever is returned by the fs_attrib.h functions).
368
369PROBLEMS:
370
371- pointers are 32-bits; attributes are limited to ssize_t in size so it's
372  possible to overflow... in practice, this isn't too likely... your
373  machine will thrash like hell before that happens
374
375*/
376
377#define INITIAL_BUFF_SIZE 65536
378
379int get_attr_dir( const char *name, char **attr_buff, off_t *total_size )
380{
381    int               retval = EOK;
382    int               fd;
383    DIR              *fa_dir;
384    struct dirent    *fa_ent;
385    off_t             attrs_size = 0;
386    size_t           entname_size;
387    char             *ptr;
388    struct attr_info  fa_info;
389
390    *total_size = 0;
391
392    /* ----------------------------------------------------------------- */
393    /* Sanity-check.                                                     */
394    if( *attr_buff != NULL ) {
395        return EINVAL;
396    }
397
398    /* ----------------------------------------------------------------- */
399    /* Can we open the file/directory?                                   */
400    /*                                                                   */
401    /* linkput is a zip global; it's set to 1 if we're storing symbolic  */
402    /* links as symbolic links (instead of storing the thing the link    */
403    /* points to)... if we're storing the symbolic link as a link, we'll */
404    /* want the link's file attributes, otherwise we want the target's.  */
405
406    fd = open( name, linkput ? O_RDONLY | O_NOTRAVERSE : O_RDONLY );
407    if( fd < 0 ) {
408        return errno;
409    }
410
411    /* ----------------------------------------------------------------- */
412    /* Allocate an initial buffer; 64k should usually be enough.         */
413    *attr_buff = (char *)malloc( INITIAL_BUFF_SIZE );
414    ptr        = *attr_buff;
415    if( ptr == NULL ) {
416        close( fd );
417
418        return ENOMEM;
419    }
420
421    /* ----------------------------------------------------------------- */
422    /* Open the attributes directory for this file.                      */
423    fa_dir = open_attrdir( fd );
424    if( fa_dir == NULL ) {
425        close( fd );
426
427        free( ptr );
428        *attr_buff = NULL;
429
430        return retval;
431    }
432
433    /* ----------------------------------------------------------------- */
434    /* Read all the attributes; the buffer could grow > 64K if there are */
435    /* many and/or they are large.                                       */
436    while( ( fa_ent = read_attrdir( fa_dir ) ) != NULL ) {
437        retval = stat_attr( fd, fa_ent->d_name, &fa_info );
438        /* TODO: check retval != EOK */
439
440        entname_size = strlen( fa_ent->d_name ) + 1;
441        attrs_size += entname_size + sizeof( struct attr_info ) + fa_info.ai_size;
442
443        if( attrs_size > INITIAL_BUFF_SIZE ) {
444            unsigned long offset = ptr - *attr_buff;
445
446            *attr_buff = (char *)realloc( *attr_buff, attrs_size );
447            if( *attr_buff == NULL ) {
448                retval = close_attrdir( fa_dir );
449                /* TODO: check retval != EOK */
450                close( fd );
451                return ENOMEM;
452            }
453
454            ptr = *attr_buff + offset;
455        }
456
457        /* Now copy the data for this attribute into the buffer. */
458        strcpy( ptr, fa_ent->d_name );
459        ptr += entname_size;
460
461  memcpy( ptr, &fa_info, sizeof( struct attr_info ) );
462        ptr += sizeof( struct attr_info );
463
464        if( fa_info.ai_size > 0 ) {
465            ssize_t read_bytes = read_attr( fd, fa_ent->d_name, fa_info.ai_type, ptr, 0, fa_info.ai_size );
466            if( read_bytes != fa_info.ai_size ) {
467                /* print a warning about mismatched sizes */
468                char buff[80];
469                sprintf( buff, "read %d, expected %d", read_bytes, (ssize_t)fa_info.ai_size );
470                zipwarn( "attribute size mismatch: ", buff );
471            }
472
473            ptr += fa_info.ai_size;
474        }
475    }
476
477    /* ----------------------------------------------------------------- */
478    /* Close the attribute directory.                                    */
479    retval = close_attrdir( fa_dir );
480    /* TODO: check retval != EOK */
481
482    /* ----------------------------------------------------------------- */
483    /* If the buffer is too big, shrink it.                              */
484    if( attrs_size < INITIAL_BUFF_SIZE ) {
485        *attr_buff = (char *)realloc( *attr_buff, attrs_size );
486        if( *attr_buff == NULL ) {
487            close( fd );
488            return ENOMEM;
489        }
490    }
491
492    *total_size = attrs_size;
493
494    close( fd );
495
496    return EOK;
497}
498
499/* ---------------------------------------------------------------------- */
500/* Add a 'UT' extra field to the zlist data pointed to by z.              */
501
502#define EB_L_UT_SIZE    (EB_HEADSIZE + EB_UT_LEN(2))
503#define EB_C_UT_SIZE    (EB_HEADSIZE + EB_UT_LEN(1))
504
505local int add_UT_ef( struct zlist far *z )
506{
507    char        *l_ef = NULL;
508    char        *c_ef = NULL;
509    struct stat  s;
510
511#ifdef IZ_CHECK_TZ
512    if (!zp_tz_is_valid)
513        return ZE_OK;           /* skip silently if no valid TZ info */
514#endif
515
516    /* We can't work if there's no entry to work on. */
517    if( z == NULL ) {
518        return ZE_LOGIC;
519    }
520
521    /* Check to make sure we've got enough room in the extra fields. */
522    if( z->ext + EB_L_UT_SIZE > USHRT_MAX ||
523        z->cext + EB_C_UT_SIZE > USHRT_MAX ) {
524        return ZE_MEM;
525    }
526
527    /* stat() the file (or the symlink) to get the data; if we can't get */
528    /* the data, there's no point in trying to fill out the fields.      */
529    if(LSSTAT( z->name, &s ) ) {
530        return ZE_OPEN;
531    }
532
533    /* Allocate memory for the local and central extra fields. */
534    if( z->extra && z->ext != 0 ) {
535        l_ef = (char *)realloc( z->extra, z->ext + EB_L_UT_SIZE );
536    } else {
537        l_ef = (char *)malloc( EB_L_UT_SIZE );
538        z->ext = 0;
539    }
540    if( l_ef == NULL ) {
541        return ZE_MEM;
542    }
543    z->extra = l_ef;
544    l_ef += z->ext;
545
546    if( z->cextra && z->cext != 0 ) {
547        c_ef = (char *)realloc( z->cextra, z->cext + EB_C_UT_SIZE );
548    } else {
549        c_ef = (char *)malloc( EB_C_UT_SIZE );
550        z->cext = 0;
551    }
552    if( c_ef == NULL ) {
553        return ZE_MEM;
554    }
555    z->cextra = c_ef;
556    c_ef += z->cext;
557
558    /* Now add the local version of the field. */
559    *l_ef++ = 'U';
560    *l_ef++ = 'T';
561    *l_ef++ = (char)(EB_UT_LEN(2)); /* length of data in local EF */
562    *l_ef++ = (char)0;
563    *l_ef++ = (char)(EB_UT_FL_MTIME | EB_UT_FL_ATIME);
564    *l_ef++ = (char)(s.st_mtime);
565    *l_ef++ = (char)(s.st_mtime >> 8);
566    *l_ef++ = (char)(s.st_mtime >> 16);
567    *l_ef++ = (char)(s.st_mtime >> 24);
568    *l_ef++ = (char)(s.st_atime);
569    *l_ef++ = (char)(s.st_atime >> 8);
570    *l_ef++ = (char)(s.st_atime >> 16);
571    *l_ef++ = (char)(s.st_atime >> 24);
572
573    z->ext += EB_L_UT_SIZE;
574
575    /* Now add the central version. */
576    memcpy(c_ef, l_ef-EB_L_UT_SIZE, EB_C_UT_SIZE);
577    c_ef[EB_LEN] = (char)(EB_UT_LEN(1)); /* length of data in central EF */
578
579    z->cext += EB_C_UT_SIZE;
580
581    return ZE_OK;
582}
583
584/* ---------------------------------------------------------------------- */
585/* Add a 'Ux' extra field to the zlist data pointed to by z.              */
586
587#define EB_L_UX2_SIZE   (EB_HEADSIZE + EB_UX2_MINLEN)
588#define EB_C_UX2_SIZE   (EB_HEADSIZE)
589
590local int add_Ux_ef( struct zlist far *z )
591{
592    char        *l_ef = NULL;
593    char        *c_ef = NULL;
594    struct stat  s;
595
596    /* Check to make sure we've got enough room in the extra fields. */
597    if( z->ext + EB_L_UX2_SIZE > USHRT_MAX ||
598        z->cext + EB_C_UX2_SIZE > USHRT_MAX ) {
599        return ZE_MEM;
600    }
601
602    /* stat() the file (or the symlink) to get the data; if we can't get */
603    /* the data, there's no point in trying to fill out the fields.      */
604    if(LSSTAT( z->name, &s ) ) {
605        return ZE_OPEN;
606    }
607
608    /* Allocate memory for the local and central extra fields. */
609    if( z->extra && z->ext != 0 ) {
610        l_ef = (char *)realloc( z->extra, z->ext + EB_L_UX2_SIZE );
611    } else {
612        l_ef = (char *)malloc( EB_L_UX2_SIZE );
613        z->ext = 0;
614    }
615    if( l_ef == NULL ) {
616        return ZE_MEM;
617    }
618    z->extra = l_ef;
619    l_ef += z->ext;
620
621    if( z->cextra && z->cext != 0 ) {
622        c_ef = (char *)realloc( z->cextra, z->cext + EB_C_UX2_SIZE );
623    } else {
624        c_ef = (char *)malloc( EB_C_UX2_SIZE );
625        z->cext = 0;
626    }
627    if( c_ef == NULL ) {
628        return ZE_MEM;
629    }
630    z->cextra = c_ef;
631    c_ef += z->cext;
632
633    /* Now add the local version of the field. */
634    *l_ef++ = 'U';
635    *l_ef++ = 'x';
636    *l_ef++ = (char)(EB_UX2_MINLEN);
637    *l_ef++ = (char)(EB_UX2_MINLEN >> 8);
638    *l_ef++ = (char)(s.st_uid);
639    *l_ef++ = (char)(s.st_uid >> 8);
640    *l_ef++ = (char)(s.st_gid);
641    *l_ef++ = (char)(s.st_gid >> 8);
642
643    z->ext += EB_L_UX2_SIZE;
644
645    /* Now add the central version of the field. */
646    *c_ef++ = 'U';
647    *c_ef++ = 'x';
648    *c_ef++ = 0;
649    *c_ef++ = 0;
650
651    z->cext += EB_C_UX2_SIZE;
652
653    return ZE_OK;
654}
655
656/* ---------------------------------------------------------------------- */
657/* Add a 'At' extra field to the zlist data pointed to by z.              */
658
659#define EB_L_AT_SIZE    (EB_HEADSIZE + EB_L_AT_LEN) /* + attr size */
660#define EB_C_AT_SIZE    (EB_HEADSIZE + EB_C_AT_LEN)
661
662#define MEMCOMPRESS_HEADER      6   /* ush compression type, ulg CRC */
663#define DEFLAT_WORSTCASE_ADD    5   /* byte blocktype, 2 * ush blocklength */
664#define MEMCOMPRESS_OVERHEAD    (MEMCOMPRESS_HEADER + DEFLAT_WORSTCASE_ADD)
665
666local int add_At_ef( struct zlist far *z )
667{
668    char *l_ef       = NULL;
669    char *c_ef       = NULL;
670    char *attrbuff   = NULL;
671    off_t attrsize   = 0;
672    char *compbuff   = NULL;
673    ush   compsize   = 0;
674    uch   flags      = 0;
675
676    /* Check to make sure we've got enough room in the extra fields. */
677    if( z->ext + EB_L_AT_SIZE > USHRT_MAX ||
678        z->cext + EB_C_AT_SIZE > USHRT_MAX ) {
679        return ZE_MEM;
680    }
681
682    /* Attempt to load up a buffer full of the file's attributes. */
683    {
684        if (get_attr_dir( z->name, &attrbuff, &attrsize) != EOK ) {
685            return ZE_OPEN;
686        }
687        if (attrsize == 0) {
688            return ZE_OK;
689        }
690        if (attrbuff == NULL) {
691            return ZE_LOGIC;
692        }
693
694        /* Check for way too much data. */
695        if (attrsize > (off_t)ULONG_MAX) {
696            zipwarn( "uncompressed attributes truncated", "" );
697            attrsize = (off_t)(ULONG_MAX - MEMCOMPRESS_OVERHEAD);
698        }
699    }
700
701    if (verbose) {
702        printf( "\t[in=%lu]", (unsigned long)attrsize );
703    }
704
705    /* Try compressing the data */
706    compbuff = (char *)malloc( (size_t)attrsize + MEMCOMPRESS_OVERHEAD );
707    if( compbuff == NULL ) {
708        return ZE_MEM;
709    }
710    compsize = memcompress( compbuff,
711                            (size_t)attrsize + MEMCOMPRESS_OVERHEAD,
712                            attrbuff,
713                            (size_t)attrsize );
714    if (verbose) {
715        printf( " [out=%u]", compsize );
716    }
717
718    /* Attempt to optimise very small attributes. */
719    if (compsize > attrsize) {
720        free( compbuff );
721        compsize = (ush)attrsize;
722        compbuff = attrbuff;
723
724        flags = EB_AT_FL_NATURAL;
725    }
726
727    /* Check to see if we really have enough room in the EF for the data. */
728    if( ( z->ext + compsize + EB_L_AT_LEN ) > USHRT_MAX ) {
729        compsize = USHRT_MAX - EB_L_AT_LEN - z->ext;
730    }
731
732    /* Allocate memory for the local and central extra fields. */
733    if( z->extra && z->ext != 0 ) {
734        l_ef = (char *)realloc( z->extra, z->ext + EB_L_AT_SIZE + compsize );
735    } else {
736        l_ef = (char *)malloc( EB_L_AT_SIZE + compsize );
737        z->ext = 0;
738    }
739    if( l_ef == NULL ) {
740        return ZE_MEM;
741    }
742    z->extra = l_ef;
743    l_ef += z->ext;
744
745    if( z->cextra && z->cext != 0 ) {
746        c_ef = (char *)realloc( z->cextra, z->cext + EB_C_AT_SIZE );
747    } else {
748        c_ef = (char *)malloc( EB_C_AT_SIZE );
749        z->cext = 0;
750    }
751    if( c_ef == NULL ) {
752        return ZE_MEM;
753    }
754    z->cextra = c_ef;
755    c_ef += z->cext;
756
757    /* Now add the local version of the field. */
758    *l_ef++ = 'A';
759    *l_ef++ = 't';
760    *l_ef++ = (char)(compsize + EB_L_AT_LEN);
761    *l_ef++ = (char)((compsize + EB_L_AT_LEN) >> 8);
762    *l_ef++ = (char)((unsigned long)attrsize);
763    *l_ef++ = (char)((unsigned long)attrsize >> 8);
764    *l_ef++ = (char)((unsigned long)attrsize >> 16);
765    *l_ef++ = (char)((unsigned long)attrsize >> 24);
766    *l_ef++ = flags;
767    memcpy( l_ef, compbuff, (size_t)compsize );
768
769    z->ext += EB_L_AT_SIZE + compsize;
770
771    /* And the central version. */
772    *c_ef++ = 'A';
773    *c_ef++ = 't';
774    *c_ef++ = (char)(EB_C_AT_LEN);
775    *c_ef++ = (char)(EB_C_AT_LEN >> 8);
776    *c_ef++ = (char)compsize;
777    *c_ef++ = (char)(compsize >> 8);
778    *c_ef++ = (char)(compsize >> 16);
779    *c_ef++ = (char)(compsize >> 24);
780    *c_ef++ = flags;
781
782    z->cext += EB_C_AT_SIZE;
783
784    return ZE_OK;
785}
786
787/* Extra field info:
788   - 'UT' - UNIX time extra field
789   - 'Ux' - UNIX uid/gid extra field
790   - 'At' - AtheOS file attributes extra field
791
792   This is done the same way ../unix/unix.c stores the 'UT'/'Ux' fields
793   (full data in local header, only modification time in central header),
794   with the 'At' field added to the end and the size of the 'At' field
795   in the central header.
796
797   See the end of atheos/osdep.h for a simple explanation of the 'At' EF
798   layout.
799 */
800int set_extra_field(z, z_utim)
801  struct zlist far *z;
802  iztimes *z_utim;
803  /* store full data in local header but just modification time stamp info
804     in central header */
805{
806    int retval;
807
808    /* Check to make sure z is valid. */
809    if( z == NULL ) {
810        return ZE_LOGIC;
811    }
812
813    retval = add_UT_ef(z);
814    if( retval != ZE_OK ) {
815        return retval;
816    }
817
818    retval = add_Ux_ef(z);
819    if( retval != ZE_OK ) {
820        return retval;
821    }
822
823    return add_At_ef(z); /* last function; we can use return value directly */
824}
825
826/* ---------------------------------------------------------------------- */
827/* Set a file's MIME type.                                                */
828void setfiletype(const char *file, const char *type)
829{
830    int fd;
831    off_t nLen;
832    ssize_t nError;
833
834    fd = open( file, O_RDWR );
835
836    if (fd < 0) {
837        zipwarn( "can't open zipfile to write file type", "" );
838    }
839
840    else
841    {
842        nLen = strlen( type );
843        /* FIXME: write_attr() should return count of writed bytes */
844        nError = write_attr( fd, "os::MimeType", O_TRUNC, ATTR_TYPE_STRING, type, 0, nLen );
845        if (nError < 0) {
846            zipwarn( "couldn't write complete file type", "" );
847        }
848        close( fd );
849    }
850}
851
852#endif /* !UTIL */
853
854/******************************/
855/*  Function version_local()  */
856/******************************/
857
858void version_local()
859{
860    static ZCONST char CompiledWith[] = "Compiled with %s%s for %s%s%s%s.\n\n";
861
862    printf(CompiledWith,
863
864#ifdef __GNUC__
865      "gcc ", __VERSION__,
866#else
867      "(unknown compiler)", "",
868#endif
869
870      "Syllable",
871
872#if defined(i486) || defined(__i486) || defined(__i486__) || defined(i386) || defined(__i386) || defined(__i386__)
873      " (x86)",
874#else
875      " (unknown platform)",
876#endif
877
878#ifdef __DATE__
879      " on ", __DATE__
880#else
881      "", ""
882#endif
883    );
884
885} /* end function version_local() */
886