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
61char 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
111unsigned 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
156typedef 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
164extern char *label;
165local ulg label_time = 0;
166local ulg label_mode = 0;
167local time_t label_utim = 0;
168
169local int relative_dir_s = 0;   /* Relative directory spec. */
170
171/* Local functions */
172local void vms_wild OF((char *, zDIR *));
173local zDIR *zopendir OF((ZCONST char *));
174local char *readd OF((zDIR *));
175local char *strlower OF((char *));
176local 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
184int 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  }
206return ((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
227local 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 */
307local 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*/
359int 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*/
494local 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
547static char wild_version_part[10]="\0";
548
549local 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
598local 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
644local 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
677int 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
725int 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
799local 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
809local 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
819char *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
1110char *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
1315void 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
1346ulg 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
1424int 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