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