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 
41 extern char *label;
42 local ulg label_time = 0;
43 local ulg label_mode = 0;
44 local time_t label_utim = 0;
45 
46 /* Local functions */
47 local char *readd OF((DIR *));
48 local int get_attr_dir( const char *, char **, off_t * );
49 local int add_UT_ef( struct zlist far * );
50 local int add_Ux_ef( struct zlist far * );
51 local int add_At_ef( struct zlist far * );
52 
readd(d)53 local char *readd(d)
54 DIR *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 
procname(n,caseflag)64 int procname(n, caseflag)
65 char *n;                /* name to process */
66 int 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 
ex2in(x,isdir,pdosflag)156 char *ex2in(x, isdir, pdosflag)
157 char *x;                /* external file name */
158 int isdir;              /* input: x is a directory */
159 int *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 
in2ex(n)209 char *in2ex(n)
210 char *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  */
stamp(f,d)225 void stamp(f, d)
226 char *f;                /* name of file to change */
227 ulg 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 
filetime(f,a,n,t)247 ulg filetime(f, a, n, t)
248 char *f;                /* name of file to get info on */
249 ulg *a;                 /* return value: file attributes */
250 long *n;                /* return value: file size */
251 iztimes *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 
356 Return a malloc()'d buffer containing all of the attributes and their names
357 for the file specified in name.  You have to free() this yourself.  The length
358 of the buffer is also returned.
359 
360 If get_attr_dir() fails, the buffer will be NULL, total_size will be 0,
361 and 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 
367 Other errors are possible (whatever is returned by the fs_attrib.h functions).
368 
369 PROBLEMS:
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 
379 int 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 
505 local 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 
590 local 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 
666 local 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  */
800 int 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.                                                */
828 void 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 
858 void 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