1 /*
2   Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
3 
4   See the accompanying file LICENSE, version 2007-Mar-4 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 /* 2005-02-14 SMS.
11    Added some ODS5 support.
12       Use longer name structures in NAML, where available.
13       Locate special characters mindful of "^" escapes.
14       Replaced compile-time case preservation (VMS_PRESERVE_CASE macro)
15       with command-line-specified case preservation (vms_case_x
16       variables).
17    Prototyped all functions.
18    Removed "#ifndef UTIL", as no one should be compiling it that way.
19 */
20 
21 #include "zip.h"
22 #include "vmsmunch.h"
23 #include "vms.h"
24 
25 #include <ctype.h>
26 #include <time.h>
27 #include <unixlib.h>
28 
29 /* Judge availability of str[n]casecmp() in C RTL.
30    (Note: This must follow a "#include <decc$types.h>" in something to
31    ensure that __CRTL_VER is as defined as it will ever be.  DEC C on
32    VAX may not define it itself.)
33 */
34 #ifdef __CRTL_VER
35 #if __CRTL_VER >= 70000000
36 #define HAVE_STRCASECMP
37 #endif /* __CRTL_VER >= 70000000 */
38 #endif /* def __CRTL_VER */
39 
40 #ifdef HAVE_STRCASECMP
41 #include <strings.h>    /* str[n]casecmp() */
42 #endif /* def HAVE_STRCASECMP */
43 
44 #include <dvidef.h>
45 #include <lib$routines.h>
46 #include <ssdef.h>
47 #include <stsdef.h>
48 #include <starlet.h>
49 
50 /* Directory file type with version, and its strlen(). */
51 #define DIR_TYPE_VER ".DIR;1"
52 #define DIR_TYPE_VER_LEN (sizeof( DIR_TYPE_VER)- 1)
53 
54 /* Extra malloc() space in names for cutpath().  (May have to change
55    ".FOO]" to "]FOO.DIR;1".)
56 */
57 #define DIR_PAD (DIR_TYPE_VER_LEN- 1)
58 
59 /* Hex digit table. */
60 
61 char hex_digit[ 16] = {
62  '0', '1', '2', '3', '4', '5', '6', '7',
63  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
64 };
65 
66 /* Character property table for (re-)escaping ODS5 extended file names.
67    Note that this table ignore Unicode, and does not identify invalid
68    characters.
69 
70    ODS2 valid characters: 0-9 A-Z a-z $ - _
71 
72    ODS5 Invalid characters:
73       C0 control codes (0x00 to 0x1F inclusive)
74       Asterisk (*)
75       Question mark (?)
76 
77    ODS5 Invalid characters only in VMS V7.2 (which no one runs, right?):
78       Double quotation marks (")
79       Backslash (\)
80       Colon (:)
81       Left angle bracket (<)
82       Right angle bracket (>)
83       Slash (/)
84       Vertical bar (|)
85 
86    Characters escaped by "^":
87       SP  !  #  %  &  '  (  )  +  ,  .  ;  =  @  [  ]  ^  `  {  }  ~
88 
89    Either "^_" or "^ " is accepted as a space.  Period (.) is a special
90    case.  Note that un-escaped < and > can also confuse a directory
91    spec.
92 
93    Characters put out as ^xx:
94       7F (DEL)
95       80-9F (C1 control characters)
96       A0 (nonbreaking space)
97       FF (Latin small letter y diaeresis)
98 
99    Other cases:
100       Unicode: "^Uxxxx", where "xxxx" is four hex digits.
101 
102     Property table values:
103       Normal escape:    1
104       Space:            2
105       Dot:              4
106       Hex-hex escape:   8
107       -------------------
108       Hex digit:       64
109 */
110 
111 unsigned char char_prop[ 256] = {
112 
113 /* NUL SOH STX ETX EOT ENQ ACK BEL   BS  HT  LF  VT  FF  CR  SO  SI */
114     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
115 
116 /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB  CAN  EM SUB ESC  FS  GS  RS  US */
117     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
118 
119 /*  SP  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /  */
120     2,  1,  0,  1,  0,  1,  1,  1,   1,  1,  0,  1,  1,  0,  4,  0,
121 
122 /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?  */
123    64, 64, 64, 64, 64, 64, 64, 64,  64, 64,  0,  1,  1,  1,  1,  1,
124 
125 /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O  */
126     1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,
127 
128 /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _  */
129     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  0,
130 
131 /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o  */
132     1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,
133 
134 /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~  DEL */
135     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  8,
136 
137     8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
138     8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
139     8,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
140     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
141     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
142     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
143     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
144     0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  8
145 };
146 
147 /* The C RTL from OpenVMS 7.0 and newer supplies POSIX compatible versions of
148  * opendir() et al. Thus, we have to use other names in our private code for
149  * directory scanning to prevent symbol name conflicts at link time.
150  * For now, we do not use the library supplied "dirent.h" functions, since
151  * our private implementation provides some functionality which may not be
152  * present in the library versions.  For example:
153  * ==> zopendir("DISK:[DIR.SUB1]SUB2.DIR") scans "DISK:[DIR.SUB1.SUB2]".
154  */
155 
156 typedef struct zdirent {
157   int d_wild;                /* flag for wildcard vs. non-wild */
158   struct FAB fab;
159   struct NAM_STRUCT nam;
160   char d_qualwildname[ NAM_MAXRSS+ 1];
161   char d_name[ NAM_MAXRSS+ 1];
162 } zDIR;
163 
164 extern char *label;
165 local ulg label_time = 0;
166 local ulg label_mode = 0;
167 local time_t label_utim = 0;
168 
169 local int relative_dir_s = 0;   /* Relative directory spec. */
170 
171 /* Local functions */
172 local void vms_wild OF((char *, zDIR *));
173 local zDIR *zopendir OF((ZCONST char *));
174 local char *readd OF((zDIR *));
175 local char *strlower OF((char *));
176 local char *strupper OF((char *));
177 
178 /* 2004-09-25 SMS.
179    str[n]casecmp() replacement for old C RTL.
180    Assumes a prehistorically incompetent toupper().
181 */
182 #ifndef HAVE_STRCASECMP
183 
strncasecmp(char * s1,char * s2,size_t n)184 int strncasecmp( char *s1, char *s2, size_t n)
185 {
186   /* Initialization prepares for n == 0. */
187   char c1 = '\0';
188   char c2 = '\0';
189 
190   while (n-- > 0)
191   {
192     /* Set c1 and c2.  Convert lower-case characters to upper-case. */
193     if (islower( c1 = *s1))
194       c1 = toupper( c1);
195 
196     if (islower( c2 = *s2))
197       c2 = toupper( c2);
198 
199     /* Quit at inequality or NUL. */
200     if ((c1 != c2) || (c1 == '\0'))
201       break;
202 
203     s1++;
204     s2++;
205   }
206 return ((unsigned int) c1- (unsigned int) c2);
207 }
208 
209 #ifndef UINT_MAX
210 #define UINT_MAX 4294967295U
211 #endif
212 
213 #define strcasecmp( s1, s2) strncasecmp( s1, s2, UINT_MAX)
214 
215 #endif /* ndef HAVE_STRCASECMP */
216 
217 
218 /* 2004-09-27 SMS.
219    eat_carets().
220 
221    Delete ODS5 extended file name escape characters ("^") in the
222    original buffer.
223    Note that the current scheme does not handle all EFN cases, but it
224    could be made more complicated.
225 */
226 
eat_carets(char * str)227 local void eat_carets( char *str)
228 /* char *str;      Source pointer. */
229 {
230   char *strd;   /* Destination pointer. */
231   char hdgt;
232   unsigned char uchr;
233   unsigned char prop;
234 
235   /* Skip ahead to the first "^", if any. */
236   while ((*str != '\0') && (*str != '^'))
237      str++;
238 
239   /* If no caret was found, quit early. */
240   if (*str != '\0')
241   {
242     /* Shift characters leftward as carets are found. */
243     strd = str;
244     while (*str != '\0')
245     {
246       uchr = *str;
247       if (uchr == '^')
248       {
249         /* Found a caret.  Skip it, and check the next character. */
250         uchr = *(++str);
251         prop = char_prop[ uchr];
252         if (prop& 64)
253         {
254           /* Hex digit.  Get char code from this and next hex digit. */
255           if (uchr <= '9')
256           {
257             hdgt = uchr- '0';           /* '0' - '9' -> 0 - 9. */
258           }
259           else
260           {
261             hdgt = ((uchr- 'A')& 7)+ 10;    /* [Aa] - [Ff] -> 10 - 15. */
262           }
263           hdgt <<= 4;                   /* X16. */
264           uchr = *(++str);              /* Next char must be hex digit. */
265           if (uchr <= '9')
266           {
267             uchr = hdgt+ uchr- '0';
268           }
269           else
270           {
271             uchr = hdgt+ ((uchr- 'A')& 15)+ 10;
272           }
273         }
274         else if (uchr == '_')
275         {
276           /* Convert escaped "_" to " ". */
277           uchr = ' ';
278         }
279         else if (uchr == '/')
280         {
281           /* Convert escaped "/" (invalid Zip) to "?" (invalid VMS). */
282           uchr = '?';
283         }
284         /* Else, not a hex digit.  Must be a simple escaped character
285            (or Unicode, which is not yet handled here).
286         */
287       }
288       /* Else, not a caret.  Use as-is. */
289       *strd = uchr;
290 
291       /* Advance destination and source pointers. */
292       strd++;
293       str++;
294     }
295     /* Terminate the destination string. */
296     *strd = '\0';
297   }
298 }
299 
300 
301 /* 2007-05-22 SMS.
302  * explicit_dev().
303  *
304  * Determine if an explicit device name is present in a (VMS) file
305  * specification.
306  */
explicit_dev(char * file_spec)307 local int explicit_dev( char *file_spec)
308 {
309   int sts;
310   struct FAB fab;               /* FAB. */
311   struct NAM_STRUCT nam;        /* NAM[L]. */
312 
313   /* Initialize the FAB and NAM[L], and link the NAM[L] to the FAB. */
314   nam = CC_RMS_NAM;
315   fab = cc$rms_fab;
316   fab.FAB_NAM = &nam;
317 
318   /* Point the FAB/NAM[L] fields to the actual name and default name. */
319 
320 #ifdef NAML$C_MAXRSS
321 
322   fab.fab$l_dna = (char *) -1;  /* Using NAML for default name. */
323   fab.fab$l_fna = (char *) -1;  /* Using NAML for file name. */
324 
325 #endif /* def NAML$C_MAXRSS */
326 
327   /* File name. */
328   FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = file_spec;
329   FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen( file_spec);
330 
331   nam.NAM_NOP = NAM_M_SYNCHK;   /* Syntax-only analysis. */
332   sts = sys$parse( &fab, 0, 0); /* Parse the file spec. */
333 
334   /* Device found = $PARSE success and "device was explicit" flag. */
335   return (((sts& STS$M_SEVERITY) == STS$M_SUCCESS) &&
336    ((nam.NAM_FNB& NAM_M_EXP_DEV) != 0));
337 }
338 
339 
340 /* 2005-02-04 SMS.
341    find_dir().
342 
343    Find directory boundaries in an ODS2 or ODS5 file spec.
344    Returns length (zero if no directory, negative if error),
345    and sets "start" argument to first character (typically "[") location.
346 
347    No one will care about the details, but the return values are:
348 
349        0  No dir.
350       -2  [, no end.              -3  <, no end.
351       -4  [, multiple start.      -5  <, multiple start.
352       -8  ], no start.            -9  >, no start.
353      -16  ], wrong end.          -17  >, wrong end.
354      -32  ], multiple end.       -33  >, multiple end.
355 
356    Note that the current scheme handles only simple EFN cases, but it
357    could be made more complicated.
358 */
find_dir(char * file_spec,char ** start)359 int find_dir( char *file_spec, char **start)
360 {
361   char *cp;
362   char chr;
363 
364   char *end_tmp = NULL;
365   char *start_tmp = NULL;
366   int lenth = 0;
367 
368   for (cp = file_spec; cp < file_spec+ strlen( file_spec); cp++)
369   {
370     chr = *cp;
371     if (chr == '^')
372     {
373       /* Skip ODS5 extended name escaped characters. */
374       cp++;
375       /* If escaped char is a hex digit, skip the second hex digit, too. */
376       if (char_prop[ (unsigned char) *cp]& 64)
377         cp++;
378     }
379     else if (chr == '[')
380     {
381       /* Found start. */
382       if (start_tmp == NULL)
383       {
384         /* First time.  Record start location. */
385         start_tmp = cp;
386         /* Error if no end. */
387         lenth = -2;
388       }
389       else
390       {
391         /* Multiple start characters.  */
392         lenth = -4;
393         break;
394       }
395     }
396     else if (chr == '<')
397     {
398       /* Found start. */
399       if (start_tmp == NULL)
400       {
401         /* First time.  Record start location. */
402         start_tmp = cp;
403         /* Error if no end. */
404         lenth = -3;
405       }
406       else
407       {
408         /* Multiple start characters.  */
409         lenth = -5;
410         break;
411       }
412     }
413     else if (chr == ']')
414     {
415       /* Found end. */
416       if (end_tmp == NULL)
417       {
418         /* First time. */
419         if (lenth == 0)
420         {
421           /* End without start. */
422           lenth = -8;
423           break;
424         }
425         else if (lenth != -2)
426         {
427           /* Wrong kind of end. */
428           lenth = -16;
429           break;
430         }
431         /* End ok.  Record end location. */
432         end_tmp = cp;
433         lenth = end_tmp+ 1- start_tmp;
434         /* Could break here, ignoring excessive end characters. */
435       }
436       else
437       {
438         /* Multiple end characters. */
439         lenth = -32;
440         break;
441       }
442     }
443     else if (chr == '>')
444     {
445       /* Found end. */
446       if (end_tmp == NULL)
447       {
448         /* First time. */
449         if (lenth == 0)
450         {
451           /* End without start. */
452           lenth = -9;
453           break;
454         }
455         else if (lenth != -3)
456         {
457           /* Wrong kind of end. */
458           lenth = -17;
459           break;
460         }
461         /* End ok.  Record end location. */
462         end_tmp = cp;
463         lenth = end_tmp+ 1- start_tmp;
464         /* Could break here, ignoring excessive end characters. */
465       }
466       else
467       {
468         /* Multiple end characters. */
469         lenth = -33;
470         break;
471       }
472     }
473   }
474 
475   /* If both start and end were found,
476      then set result pointer where safe.
477   */
478   if (lenth > 0)
479   {
480     if (start != NULL)
481     {
482       *start = start_tmp;
483     }
484   }
485   return lenth;
486 }
487 
488 
489 /* 2005-02-08 SMS.
490    file_sys_type().
491 
492    Determine the file system type for the (VMS) path name argument.
493 */
file_sys_type(char * path)494 local int file_sys_type( char *path)
495 {
496   int acp_code;
497 
498 #ifdef DVI$C_ACP_F11V5
499 
500 /* Should know about ODS5 file system.  Do actual check.
501    (This should be non-VAX with __CRTL_VER >= 70200000.)
502 */
503 
504   int sts;
505 
506   struct dsc$descriptor_s dev_descr =
507    { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0 };
508 
509   /* Load path argument into device descriptor. */
510   dev_descr.dsc$a_pointer = path;
511   dev_descr.dsc$w_length = strlen( dev_descr.dsc$a_pointer);
512 
513   /* Get filesystem type code.
514      (Text results for this item code have been unreliable.)
515   */
516   sts = lib$getdvi( &((int) DVI$_ACPTYPE), 0, &dev_descr, &acp_code, 0, 0);
517 
518   if ((sts & STS$M_SUCCESS) != STS$K_SUCCESS)
519   {
520     acp_code = -1;
521   }
522 
523 #else /* def DVI$C_ACP_F11V5 */
524 
525 /* Too old for ODS5 file system.  Must be ODS2. */
526 
527   acp_code = DVI$C_ACP_F11V2;
528 
529 #endif /* def DVI$C_ACP_F11V5 */
530 
531   return acp_code;
532 }
533 
534 /*---------------------------------------------------------------------------
535 
536     _vms_findfirst() and _vms_findnext(), based on public-domain DECUS C
537     fwild() and fnext() routines (originally written by Martin Minow, poss-
538     ibly modified by Jerry Leichter for bintnxvms.c), were written by Greg
539     Roelofs and are still in the public domain.  Routines approximate the
540     behavior of MS-DOS (MSC and Turbo C) findfirst and findnext functions.
541 
542     2005-01-04 SMS.
543     Changed to use NAML instead of NAM, where available.
544 
545   ---------------------------------------------------------------------------*/
546 
547 static char wild_version_part[10]="\0";
548 
vms_wild(char * p,zDIR * d)549 local void vms_wild( char *p, zDIR *d)
550 {
551   /*
552    * Do wildcard setup.
553    */
554   /* Set up the FAB and NAM[L] blocks. */
555   d->fab = cc$rms_fab;                  /* Initialize FAB. */
556   d->nam = CC_RMS_NAM;                  /* Initialize NAM[L]. */
557 
558   d->fab.FAB_NAM = &d->nam;             /* FAB -> NAM[L] */
559 
560 #ifdef NAML$C_MAXRSS
561 
562   d->fab.fab$l_dna =(char *) -1;        /* Using NAML for default name. */
563   d->fab.fab$l_fna = (char *) -1;       /* Using NAML for file name. */
564 
565 #endif /* def NAML$C_MAXRSS */
566 
567   /* Argument file name and length. */
568   d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = p;
569   d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen(p);
570 
571 #define DEF_DEVDIR "SYS$DISK:[]"
572 
573   /* Default file spec and length. */
574   d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_DNA = DEF_DEVDIR;
575   d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_DNS = sizeof( DEF_DEVDIR)- 1;
576 
577   d->nam.NAM_ESA = d->d_qualwildname;   /* qualified wild name */
578   d->nam.NAM_ESS = NAM_MAXRSS;          /* max length */
579   d->nam.NAM_RSA = d->d_name;           /* matching file name */
580   d->nam.NAM_RSS = NAM_MAXRSS;          /* max length */
581 
582   /* parse the file name */
583   if (sys$parse(&d->fab) != RMS$_NORMAL)
584     return;
585   /* Does this replace d->fab.fab$l_fna with a new string in its own space?
586      I sure hope so, since p is free'ed before this routine returns. */
587 
588   /* have qualified wild name (i.e., disk:[dir.subdir]*.*); null-terminate
589    * and set wild-flag */
590   d->d_qualwildname[d->nam.NAM_ESL] = '\0';
591   d->d_wild = (d->nam.NAM_FNB & NAM$M_WILDCARD)? 1 : 0;   /* not used... */
592 #ifdef DEBUG
593   fprintf(mesg, "  incoming wildname:  %s\n", p);
594   fprintf(mesg, "  qualified wildname:  %s\n", d->d_qualwildname);
595 #endif /* DEBUG */
596 }
597 
zopendir(ZCONST char * n)598 local zDIR *zopendir( ZCONST char *n)
599 /* ZCONST char *n;         directory to open */
600 /* Start searching for files in the VMS directory n */
601 {
602   char *c;              /* scans VMS path */
603   zDIR *d;              /* malloc'd return value */
604   int m;                /* length of name */
605   char *p;              /* malloc'd temporary string */
606 
607   if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
608       (p = malloc((m = strlen(n)) + 4)) == NULL) {
609     if (d != NULL) free((zvoid *)d);
610     return NULL;
611   }
612   /* Directory may be in form "[DIR.SUB1.SUB2]" or "[DIR.SUB1]SUB2.DIR;1".
613      If latter, convert to former.
614      2005-01-31 SMS.  Changed to require ";1", as VMS does, which
615      simplified the code slightly, too.  Note that ODS5 allows ".DIR" in
616      any case (upper, lower, mixed).
617   */
618   if ((m > 0) && (*(c = strcpy(p,n)+m-1) != ']'))
619   {
620     if ((c- p < DIR_TYPE_VER_LEN) ||
621      strcasecmp((c+ 1- DIR_TYPE_VER_LEN), DIR_TYPE_VER))
622     {
623       free((zvoid *)d);  free((zvoid *)p);
624       return NULL;
625     }
626     c -= 4;             /* The "D". */
627     *c-- = '\0';        /* terminate at "DIR;1" */
628     *c = ']';           /* "." --> "]" */
629 
630     /* Replace the formerly last "]" with ".".
631        For ODS5, ignore "^]".
632     */
633     while ((c > p) && ((*--c != ']') || (*(c- 1) == '^')))
634       ;
635     *c = '.';           /* "]" --> "." */
636   }
637   strcat(p, "*.*");
638   strcat(p, wild_version_part);
639   vms_wild(p, d);       /* set up wildcard */
640   free((zvoid *)p);
641   return d;
642 }
643 
readd(zDIR * d)644 local char *readd( zDIR *d)
645 /* zDIR *d;                directory stream to read from */
646 /* Return a pointer to the next name in the directory stream d, or NULL if
647    no more entries or an error occurs. */
648 {
649   int r;                /* return code */
650 
651   do {
652     d->fab.fab$w_ifi = 0;       /* internal file index:  what does this do? */
653 /*
654   2005-02-04 SMS.
655 
656   From the docs:
657 
658         Note that you must close the file before invoking the Search
659         service (FAB$W_IFI must be 0).
660 
661   The same is true for PARSE.  Most likely, it's cleared by setting
662   "fab = cc$rms_fab", and left that way, so clearing it here may very
663   well be pointless.  (I think it is, and I've never seen it explicitly
664   cleared elsewhere, but I haven't tested it everywhere either.)
665 */
666     /* get next match to possible wildcard */
667     if ((r = sys$search(&d->fab)) == RMS$_NORMAL)
668     {
669         d->d_name[d->nam.NAM_RSL] = '\0';   /* null terminate */
670         return (char *)d->d_name;   /* OK */
671     }
672   } while (r == RMS$_PRV);
673   return NULL;
674 }
675 
676 
wild(char * p)677 int wild( char *p)
678 /* char *p;                path/pattern to match */
679 /* Expand the pattern based on the contents of the file system.
680    Return an error code in the ZE_ class.
681    Note that any command-line file argument may need wildcard expansion,
682    so all user-specified constituent file names pass through here.
683 */
684 {
685   zDIR *d;              /* stream for reading directory */
686   char *e;              /* name found in directory */
687   int f;                /* true if there was a match */
688 
689   int dir_len;          /* Length of the directory part of the name. */
690   char *dir_start;      /* First character of the directory part. */
691 
692   /* special handling of stdin request */
693   if (strcmp(p, "-") == 0)   /* if compressing stdin */
694     return newname(p, 0, 0);
695 
696   /* Determine whether this name has an absolute or relative directory
697      spec.  It's relative if there is no directory, or if the directory
698      has a leading dot ("[.").
699   */
700   dir_len = find_dir( p, &dir_start);
701   relative_dir_s = ((dir_len <= 0)? 1 : (dir_start[ 1] == '.'));
702 
703   /* Search given pattern for matching names */
704   if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL)
705     return ZE_MEM;
706   vms_wild(p, d);       /* pattern may be more than just directory name */
707 
708   /*
709    * Save version specified by user to use in recursive drops into
710    * subdirectories.
711    */
712   strncpy(wild_version_part, d->nam.NAM_L_VER, d->nam.NAM_B_VER);
713   wild_version_part[d->nam.NAM_B_VER] = '\0';
714 
715   f = 0;
716   while ((e = readd(d)) != NULL)        /* "dosmatch" is already built in */
717     if (procname(e, 0) == ZE_OK)
718       f = 1;
719   free(d);
720 
721   /* Done */
722   return f ? ZE_OK : ZE_MISS;
723 }
724 
procname(char * n,int caseflag)725 int procname( char *n, int caseflag)
726 /* char *n;                name to process */
727 /* int caseflag;           true to force case-sensitive match */
728 /* Process a name or sh expression to operate on (or exclude).  Return
729    an error code in the ZE_ class. */
730 {
731   zDIR *d;              /* directory stream from zopendir() */
732   char *e;              /* pointer to name from readd() */
733   int m;                /* matched flag */
734   char *p;              /* path for recursion */
735   struct stat s;        /* result of stat() */
736   struct zlist far *z;  /* steps through zfiles list */
737 
738   if (strcmp(n, "-") == 0)   /* if compressing stdin */
739     return newname(n, 0, caseflag);
740   else if (LSSTAT(n, &s)
741 #if defined(__TURBOC__) || defined(VMS) || defined(__WATCOMC__)
742            /* For these 3 compilers, stat() succeeds on wild card names! */
743            || isshexp(n)
744 #endif
745           )
746   {
747     /* Not a file or directory--search for shell expression in zip file */
748     if (caseflag) {
749       p = malloc(strlen(n) + 1);
750       if (p != NULL)
751         strcpy(p, n);
752     } else
753       p = ex2in(n, 0, (int *)NULL);     /* shouldn't affect matching chars */
754     m = 1;
755     for (z = zfiles; z != NULL; z = z->nxt) {
756       if (MATCH(p, z->iname, caseflag))
757       {
758         z->mark = pcount ? filter(z->zname, caseflag) : 1;
759         if (verbose)
760             fprintf(mesg, "zip diagnostic: %scluding %s\n",
761                z->mark ? "in" : "ex", z->name);
762         m = 0;
763       }
764     }
765     free((zvoid *)p);
766     return m ? ZE_MISS : ZE_OK;
767   }
768 
769   /* Live name--use if file, recurse if directory */
770   if ((s.st_mode & S_IFDIR) == 0)
771   {
772     /* add or remove name of file */
773     if ((m = newname(n, 0, caseflag)) != ZE_OK)
774       return m;
775   } else {
776     if (dirnames && (m = newname(n, 1, caseflag)) != ZE_OK) {
777       return m;
778     }
779     /* recurse into directory */
780     if (recurse && (d = zopendir(n)) != NULL)
781     {
782       while ((e = readd(d)) != NULL) {
783         if ((m = procname(e, caseflag)) != ZE_OK)     /* recurse on name */
784         {
785           free(d);
786           return m;
787         }
788       }
789       free(d);
790     }
791   } /* (s.st_mode & S_IFDIR) == 0) */
792   return ZE_OK;
793 }
794 
795 /* 2004-09-24 SMS.
796    Cuter strlower() and strupper() functions.
797 */
798 
strlower(char * s)799 local char *strlower( char *s)
800 /* Convert all uppercase letters to lowercase in string s */
801 {
802   for ( ; *s != '\0'; s++)
803     if (isupper( *s))
804       *s = tolower( *s);
805 
806   return s;
807 }
808 
strupper(char * s)809 local char *strupper( char *s)
810 /* Convert all lowercase letters to uppercase in string s */
811 {
812   for ( ; *s != '\0'; s++)
813     if (islower( *s))
814       *s = toupper( *s);
815 
816   return s;
817 }
818 
ex2in(char * x,int isdir,int * pdosflag)819 char *ex2in( char *x, int isdir, int *pdosflag)
820 /* char *x;                external file name */
821 /* int isdir;              input: x is a directory */
822 /* int *pdosflag;          output: force MSDOS file attributes? */
823 
824 /* Convert the external file name to a zip file name, returning the
825    malloc'ed string or NULL if not enough memory.
826 
827    2005-02-09 SMS.
828    Added some ODS5 support.
829 
830    Note that if we were really clever, we'd save the truncated original
831    file name for later use as "iname", instead of running the de-escaped
832    product back through in2ex() to recover it later.
833 
834    2005-11-13 SMS.
835    Changed to translate "[..." into enough "/" characters to cause
836    in2ex() to reconstruct it.  This should not be needed, however, as
837    pattern matching really should avoid ex2in() and in2ex().
838 */
839 {
840   char *n;                      /* Internal file name (malloc'ed). */
841   char *nn;                     /* Temporary "n"-like pointer. */
842   char *ext_dir_and_name;       /* External dir]name (less "dev:["). */
843   char chr;                     /* Temporary character storage. */
844   int dosflag;
845   int down_case;                /* Resultant down-case flag. */
846   int dir_len;                  /* Directory spec length. */
847   int ods_level;                /* File system type. */
848 
849   dosflag = dosify; /* default for non-DOS and non-OS/2 */
850 
851   /* Locate the directory part of the external name. */
852   dir_len = find_dir( x, &ext_dir_and_name);
853   if (dir_len <= 0)
854   {
855     /* Directory not found.  Use whole external name. */
856     ext_dir_and_name = x;
857   }
858   else if (pathput)
859   {
860     /* Include directory. */
861     if (ext_dir_and_name[ 1] == '.')
862     {
863       /* Relative path.  If not a directory-depth wildcard, then drop
864          first "[." (or "<.").  If "[..." (or "<..."), then preserve all
865          characters, including the first "[" (or "<") for special
866          handling below.
867       */
868       if ((ext_dir_and_name[ 2] != '.') || (ext_dir_and_name[ 3] != '.'))
869       {
870         /* Normal relative path.  Drop first "[." (or "<."). */
871         dir_len -= 2;
872         ext_dir_and_name += 2;
873       }
874     }
875     else
876     {
877       /* Absolute path.  Skip first "[" (or "<"). */
878       dir_len -= 1;
879       ext_dir_and_name += 1;
880 
881       /* 2007-04-26 SMS.
882          Skip past "000000." or "000000]" (or "000000>"), which should
883          not be stored in the archive.  This arises, for example, with
884          "zip -r archive [000000]foo.dir"
885       */
886 #define MFD "000000"
887 
888       if ((strncmp( ext_dir_and_name, MFD, strlen( MFD)) == 0) &&
889        ((ext_dir_and_name[ 6] == '.') ||
890        (ext_dir_and_name[ 6] == ']') ||
891        (ext_dir_and_name[ 6] == '>')))
892       {
893         dir_len -= 7;
894         ext_dir_and_name += 7;
895       }
896     }
897   }
898   else
899   {
900     /* Junking paths.  Skip the whole directory spec. */
901     ext_dir_and_name += dir_len;
902     dir_len = 0;
903   }
904 
905   /* Malloc space for internal name and copy it. */
906   if ((n = malloc(strlen( ext_dir_and_name)+ 1)) == NULL)
907     return NULL;
908   strcpy( n, ext_dir_and_name);
909 
910   /* Convert VMS directory separators (".") to "/". */
911   if (dir_len > 0)
912   {
913     for (nn = n; nn < n+ dir_len; nn++)
914     {
915       chr = *nn;
916       if (chr == '^')
917       {
918         /* Skip ODS5 extended name escaped characters. */
919         nn++;
920         /* If escaped char is a hex digit, skip the second hex digit, too. */
921         if (char_prop[ (unsigned char) *nn]& 64)
922           nn++;
923       }
924       else if ((chr == '.') || ((nn == n) && ((chr == '[') || (chr == '<'))))
925       {
926         /* Convert VMS directory separator (".", or initial "[" or "<"
927            of "[..." or "<...") to "/".
928         */
929         *nn = '/';
930       }
931     }
932     /* Replace directory end character (typically "]") with "/". */
933     n[ dir_len- 1] = '/';
934   }
935 
936   /* If relative path, then strip off the current directory. */
937   if (relative_dir_s)
938   {
939     char cwd[ NAM_MAXRSS+ 1];
940     char *cwd_dir_only;
941     char *q;
942     int cwd_dir_only_len;
943 
944     q = getcwd( cwd, (sizeof( cwd)- 1));
945 
946     /* 2004-09-24 SMS.
947        With SET PROCESSS /PARSE = EXTENDED, getcwd() can return a
948        mixed-case result, confounding the comparisons below with an
949        all-uppercase name in "n".  Always use a case-insensitive
950        comparison around here.
951     */
952 
953     /* Locate the directory part of the external name. */
954     dir_len = find_dir( q, &cwd_dir_only);
955     if (dir_len > 0)
956     {
957       /* Skip first "[" (or "<"). */
958       cwd_dir_only++;
959       /* Convert VMS directory separators (".") to "/". */
960       for (q = cwd_dir_only; q < cwd_dir_only+ dir_len; q++)
961       {
962         chr = *q;
963         if (chr == '^')
964         {
965           /* Skip ODS5 extended name escaped characters. */
966           q++;
967           /* If escaped char is a hex digit, skip the second hex digit, too. */
968           if (char_prop[ (unsigned char) *q]& 64)
969             q++;
970         }
971         else if (chr == '.')
972         {
973           /* Convert VMS directory separator (".") to "/". */
974           *q = '/';
975         }
976       }
977       /* Replace directory end character (typically "]") with "/". */
978       cwd_dir_only[ dir_len- 2] = '/';
979     }
980 
981     /* If the slash-converted cwd matches the front of the internal
982        name, then shuffle the remainder of the internal name to the
983        beginning of the internal name storage.
984 
985        Because we already know that the path is relative, this test may
986        always succeed.
987     */
988     cwd_dir_only_len = strlen( cwd_dir_only);
989     if (strncasecmp( n, cwd_dir_only, cwd_dir_only_len) == 0)
990     {
991        nn = n+ cwd_dir_only_len;
992        q = n;
993        while (*q++ = *nn++);
994     }
995   } /* (relative_dir_s) */
996 
997   /* 2007-05-22 SMS.
998    * If a device name is present, assume that it's a real (VMS) file
999    * specification, and do down-casing according to the ODS2 or ODS5
1000    * down-casing policy.  If no device name is present, assume that it's
1001    * a pattern ("-i", ...), and do no down-casing here.  (Case
1002    * sensitivity in patterns is handled elsewhere.)
1003    */
1004   if (explicit_dev( x))
1005   {
1006     /* If ODS5 is possible, do complicated down-case check.
1007 
1008        Note that the test for ODS2/ODS5 is misleading and over-broad.
1009        Here, "ODS2" includes anything from DVI$C_ACP_F11V1 (=1, ODS1) up
1010        to (but not including) DVI$C_ACP_F11V5 (= 11, DVI$C_ACP_F11V5),
1011        while "ODS5" includes anything from DVI$C_ACP_F11V5 on up.  See
1012        DVIDEF.H.
1013     */
1014 
1015 #if defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS)
1016 
1017     /* Check options and/or ODS level for down-case or preserve case. */
1018     down_case = 0;      /* Assume preserve case. */
1019     if ((vms_case_2 <= 0) && (vms_case_5 < 0))
1020     {
1021       /* Always down-case. */
1022       down_case = 1;
1023     }
1024     else if ((vms_case_2 <= 0) || (vms_case_5 < 0))
1025     {
1026       /* Down-case depending on ODS level.  (Use (full) external name.) */
1027       ods_level = file_sys_type( x);
1028 
1029       if (ods_level > 0)
1030       {
1031         /* Valid ODS level.  (Name (full) contains device.)
1032          * Down-case accordingly.
1033          */
1034         if (((ods_level < DVI$C_ACP_F11V5) && (vms_case_2 <= 0)) ||
1035          ((ods_level >= DVI$C_ACP_F11V5) && (vms_case_5 < 0)))
1036         {
1037           /* Down-case for this ODS level. */
1038           down_case = 1;
1039         }
1040       }
1041     }
1042 
1043 #else /* defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS) */
1044 
1045 /* No case-preserved names are possible (VAX).  Do simple down-case check. */
1046 
1047     down_case = (vms_case_2 <= 0);
1048 
1049 #endif /* defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS) [else] */
1050 
1051     /* If down-casing, convert to lower case. */
1052     if (down_case != 0)
1053     {
1054       strlower( n);
1055     }
1056   }
1057 
1058   /* Remove simple ODS5 extended file name escape characters. */
1059   eat_carets( n);
1060 
1061   if (isdir)
1062   {
1063     if (strcasecmp( (nn = n+ strlen( n)- DIR_TYPE_VER_LEN), DIR_TYPE_VER))
1064       error("directory not version 1");
1065     else
1066       if (pathput)
1067         strcpy( nn, "/");
1068       else
1069         *n = '\0';              /* directories are discarded with zip -rj */
1070   }
1071   else if (vmsver == 0)
1072   {
1073     /* If not keeping version numbers, truncate the name at the ";".
1074        (No escaped characters are expected in the version.)
1075     */
1076     if ((ext_dir_and_name = strrchr( n, ';')) != NULL)
1077       *ext_dir_and_name = '\0';
1078   }
1079   else if (vmsver > 1)
1080   {
1081     /* Keeping version numbers, but as ".nnn", not ";nnn". */
1082     if ((ext_dir_and_name = strrchr( n, ';')) != NULL)
1083       *ext_dir_and_name = '.';
1084   }
1085 
1086   /* Remove a type-less dot. */
1087   /* (Note that currently "name..ver" is not altered.) */
1088   if ((ext_dir_and_name = strrchr( n, '.')) != NULL)
1089   {
1090     if (ext_dir_and_name[ 1] == '\0')           /* "name." -> "name" */
1091       *ext_dir_and_name = '\0';
1092     else if (ext_dir_and_name[ 1] == ';')       /* "name.;ver" -> "name;ver" */
1093     {
1094       char *f = ext_dir_and_name+ 1;
1095       while (*ext_dir_and_name++ = *f++);
1096     }
1097   }
1098 
1099   if (dosify)
1100     msname(n);
1101 
1102   /* Returned malloc'ed name */
1103   if (pdosflag)
1104     *pdosflag = dosflag;
1105 
1106   return n;
1107 }
1108 
1109 
in2ex(char * n)1110 char *in2ex( char *n)
1111 /* char *n;                internal file name */
1112 /* Convert the zip file name to an external file name, returning the malloc'ed
1113    string or NULL if not enough memory. */
1114 {
1115   char *x;              /* external file name */
1116   char *t;              /* scans name */
1117   int i;
1118   char chr;
1119   char *endp;
1120   char *last_slash;
1121   char *versionp;
1122 
1123 #ifdef NAML$C_MAXRSS
1124 
1125   char buf[ NAML$C_MAXRSS+ 1];
1126   unsigned char prop;
1127   unsigned char uchr;
1128   char *last_dot;
1129 
1130 #endif /* def NAML$C_MAXRSS */
1131 
1132   /* Locate the last slash. */
1133   last_slash = strrchr( n, '/');
1134 
1135 /* If ODS5 is possible, replace escape carets in name. */
1136 
1137 #ifdef NAML$C_MAXRSS
1138 
1139   endp = n+ strlen( n);
1140 
1141   /* Locate the version delimiter, if one is expected. */
1142   if (vmsver == 0)
1143   { /* No version expected. */
1144     versionp = endp;
1145   }
1146   else
1147   {
1148     if (vmsver > 1)
1149     { /* Expect a dot-version, ".nnn".  Locate the version ".".
1150          Temporarily terminate at this dot to allow the last-dot search
1151          below to find the last non-version dot.
1152       */
1153       versionp = strrchr( n, '.');
1154       if (versionp != NULL)     /* Can't miss. */
1155       {
1156         *versionp = '\0';
1157       }
1158     }
1159     else
1160     { /* Expect a semi-colon-version, ";nnn".  Locate the ";".  */
1161       versionp = strrchr( n, ';');
1162     }
1163     if ((versionp == NULL) || (versionp < last_slash))
1164     { /* If confused, and the version delimiter was not in the name,
1165          then ignore it.
1166       */
1167       versionp = endp;
1168     }
1169   }
1170 
1171   /* No escape needed for the last dot, if it's part of the file name.
1172      All dots in a directory must be escaped.
1173   */
1174   last_dot = strrchr( n, '.');
1175 
1176   if ((last_dot != NULL) && (last_slash != NULL) && (last_dot < last_slash))
1177   {
1178     last_dot = last_slash;
1179   }
1180 
1181   /* Replace the version dot if necessary. */
1182   if ((vmsver > 1) && (versionp != NULL) && (versionp < endp))
1183   {
1184     *versionp = '.';
1185   }
1186 
1187   /* Add ODS5 escape sequences.  Leave "/" and "?" for later.
1188      The name here looks (roughly) like: dir1/dir2/a.b
1189   */
1190   t = n;
1191   x = buf;
1192   while (uchr = *t++)
1193   {
1194     /* Characters in the version do not need escaping. */
1195     if (t <= versionp)
1196     {
1197       prop = char_prop[ uchr]& 31;
1198       if (prop)
1199       {
1200         if (prop& 4)
1201         { /* Dot. */
1202           if (t < last_dot)
1203           {
1204             /* Dot which must be escaped. */
1205             *x++ = '^';
1206           }
1207         }
1208         else if (prop& 8)
1209         {
1210           /* Character needing hex-hex escape. */
1211           *x++ = '^';
1212           *x++ = hex_digit[ uchr>> 4];
1213           uchr = hex_digit[ uchr& 15];
1214         }
1215         else
1216         {
1217           /* Non-dot character which must be escaped (and simple works).
1218              "?" gains the caret but remains "?" until later.
1219              ("/" remains (unescaped) "/".)
1220           */
1221           *x++ = '^';
1222           if (prop& 2)
1223           {
1224             /* Escaped space (represented as "^_"). */
1225             uchr = '_';
1226           }
1227         }
1228       }
1229     }
1230     *x++ = uchr;
1231   }
1232   *x = '\0';
1233 
1234   /* Point "n" to altered name buffer, and re-find the last slash. */
1235   n = buf;
1236   last_slash = strrchr( n, '/');
1237 
1238 #endif /* def NAML$C_MAXRSS */
1239 
1240   if ((t = last_slash) == NULL)
1241   {
1242     if ((x = malloc(strlen(n) + 1 + DIR_PAD)) == NULL)
1243       return NULL;
1244     strcpy(x, n);
1245   }
1246   else
1247   {
1248     if ((x = malloc(strlen(n) + 3 + DIR_PAD)) == NULL)
1249       return NULL;
1250 
1251     /* Begin with "[". */
1252     x[ 0] = '[';
1253     i = 1;
1254     if (*n != '/')
1255     {
1256       /* Relative path.  Add ".". */
1257       x[ i++] = '.';
1258     }
1259     else
1260     {
1261       /* Absolute path.  Skip leading "/". */
1262       n++;
1263     }
1264     strcpy( (x+ i), n);
1265 
1266     /* Place the final ']'.  Remember where the name starts. */
1267     *(t = x + i + (t - n)) = ']';
1268     last_slash = t;
1269 
1270     /* Replace "/" with ".", and "?" with (now escaped) "/", in the
1271        directory part of the name.
1272     */
1273     while (--t > x)
1274     {
1275       chr = *t;
1276       if (chr == '/')
1277       {
1278         *t = '.';
1279       }
1280       else if (chr == '?')
1281       {
1282         *t = '/';
1283       }
1284     }
1285 
1286     /* Replace "?" with (now escaped) "/", in the non-directory part of
1287        the name.
1288     */
1289     while ((chr = *(++last_slash)) != '\0')
1290     {
1291       if (chr == '?')
1292       {
1293         *last_slash = '/';
1294       }
1295     }
1296   }
1297 
1298 /* If case preservation is impossible (VAX, say), and down-casing, then
1299    up-case.  If case preservation is possible and wasn't done, then
1300    there's no way to ensure proper restoration of original case, so
1301    don't try.  This may differ from pre-3.0 behavior.
1302 */
1303 #ifndef NAML$C_MAXRSS
1304 
1305   if (vms_case_2 <= 0)
1306   {
1307     strupper( x);
1308   }
1309 
1310 #endif /* ndef NAML$C_MAXRSS */
1311 
1312   return x;
1313 }
1314 
stamp(char * f,ulg d)1315 void stamp( char *f, ulg d)
1316 /* char *f;                name of file to change */
1317 /* ulg d;                  dos-style time to change it to */
1318 /* Set last updated and accessed time of file f to the DOS time d. */
1319 {
1320   int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year;
1321   char timbuf[24];
1322   static ZCONST char *month[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
1323                                  "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
1324   struct VMStimbuf {
1325       char *actime;           /* VMS revision date, ASCII format */
1326       char *modtime;          /* VMS creation date, ASCII format */
1327   } ascii_times;
1328 
1329   ascii_times.actime = ascii_times.modtime = timbuf;
1330 
1331   /* Convert DOS time to ASCII format for VMSmunch */
1332   tm_sec = (int)(d << 1) & 0x3e;
1333   tm_min = (int)(d >> 5) & 0x3f;
1334   tm_hour = (int)(d >> 11) & 0x1f;
1335   tm_mday = (int)(d >> 16) & 0x1f;
1336   tm_mon = ((int)(d >> 21) & 0xf) - 1;
1337   tm_year = ((int)(d >> 25) & 0x7f) + 1980;
1338   sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00", tm_mday, month[tm_mon],
1339     tm_year, tm_hour, tm_min, tm_sec);
1340 
1341   /* Set updated and accessed times of f */
1342   if (VMSmunch(f, SET_TIMES, (char *)&ascii_times) != RMS$_NMF)
1343     zipwarn("can't set zipfile time: ", f);
1344 }
1345 
filetime(char * f,ulg * a,zoff_t * n,iztimes * t)1346 ulg filetime( char *f, ulg *a, zoff_t *n, iztimes *t)
1347 /* char *f;                name of file to get info on */
1348 /* ulg *a;                 return value: file attributes */
1349 /* zoff_t *n;              return value: file size */
1350 /* iztimes *t;             return value: access, modific. and creation times */
1351 /* If file *f does not exist, return 0.  Else, return the file's last
1352    modified date and time as an MSDOS date and time.  The date and
1353    time is returned in a long with the date most significant to allow
1354    unsigned integer comparison of absolute times.  Also, if a is not
1355    a NULL pointer, store the file attributes there, with the high two
1356    bytes being the Unix attributes, and the low byte being a mapping
1357    of that to DOS attributes.  If n is not NULL, store the file size
1358    there.  If t is not NULL, the file's access, modification and creation
1359    times are stored there as UNIX time_t values.
1360    If f is "-", use standard input as the file. If f is a device, return
1361    a file size of -1 */
1362 {
1363   struct stat s;        /* results of stat() */
1364   /* convert to a malloc string dump FNMAX - 11/8/04 EG */
1365   char *name;
1366   int len = strlen(f);
1367 
1368   if (f == label) {
1369     if (a != NULL)
1370       *a = label_mode;
1371     if (n != NULL)
1372       *n = -2; /* convention for a label name */
1373     if (t != NULL)
1374       t->atime = t->mtime = t->ctime = label_utim;
1375     return label_time;
1376   }
1377   if ((name = malloc(len + 1)) == NULL) {
1378     ZIPERR(ZE_MEM, "filetime");
1379   }
1380   strcpy(name, f);
1381   if (name[len - 1] == '/')
1382     name[len - 1] = '\0';
1383   /* not all systems allow stat'ing a file with / appended */
1384 
1385   if (strcmp(f, "-") == 0) {
1386     if (fstat(fileno(stdin), &s) != 0) {
1387       free(name);
1388       error("fstat(stdin)");
1389     }
1390   } else if (LSSTAT(name, &s) != 0) {
1391              /* Accept about any file kind including directories
1392               * (stored with trailing / with -r option)
1393               */
1394     free(name);
1395     return 0;
1396   }
1397   free(name);
1398 
1399   if (a != NULL) {
1400     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
1401     if ((s.st_mode & S_IFDIR) != 0) {
1402       *a |= MSDOS_DIR_ATTR;
1403     }
1404   }
1405   if (n != NULL)
1406     *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1;
1407   if (t != NULL) {
1408     t->atime = s.st_mtime;
1409 #ifdef USE_MTIME
1410     t->mtime = s.st_mtime;            /* Use modification time in VMS */
1411 #else
1412     t->mtime = s.st_ctime;            /* Use creation time in VMS */
1413 #endif
1414     t->ctime = s.st_ctime;
1415   }
1416 
1417 #ifdef USE_MTIME
1418   return unix2dostime((time_t *)&s.st_mtime); /* Use modification time in VMS */
1419 #else
1420   return unix2dostime((time_t *)&s.st_ctime); /* Use creation time in VMS */
1421 #endif
1422 }
1423 
deletedir(char * d)1424 int deletedir( char *d)
1425 /* char *d;                directory to delete */
1426 
1427 /* Delete the directory *d if it is empty, do nothing otherwise.
1428    Return the result of rmdir(), delete(), or system().
1429    For VMS, d must be in format [x.y]z.dir;1  (not [x.y.z]).
1430  */
1431 {
1432     /* code from Greg Roelofs, who horked it from Mark Edwards (unzip) */
1433     int r, len;
1434     char *s;              /* malloc'd string for system command */
1435 
1436     len = strlen(d);
1437     if ((s = malloc(len + 34)) == NULL)
1438       return 127;
1439 
1440     system(strcat(strcpy(s, "set prot=(o:rwed) "), d));
1441     r = delete(d);
1442     free(s);
1443     return r;
1444 }
1445