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