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