1/* 2 Copyright (c) 1990-2008 Info-ZIP. All rights reserved. 3 4 See the accompanying file LICENSE, version 2007-Mar-04 or later 5 (the contents of which are also included in unzip.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/* 2004-12-13 SMS. 11 * Disabled the module name macro to accommodate old GNU C which didn't 12 * obey the directive, and thus confused MMS/MMK where the object 13 * library dependencies need to have the correct module name. 14 */ 15#if 0 16#define module_name VMS_UNZIP_CMDLINE 17#define module_ident "02-013" 18#endif /* 0 */ 19 20/* 21** 22** Facility: UNZIP 23** 24** Module: VMS_UNZIP_CMDLINE 25** 26** Author: Hunter Goatley <goathunter@MadGoat.com> 27** 28** Date: 25 Apr 97 (orig. Zip version, 30 Jul 93) 29** 30** Abstract: Routines to handle a VMS CLI interface for UnZip. The CLI 31** command line is parsed and a new argc/argv are built and 32** returned to UnZip. 33** 34** Modified by: 35** 36** 02-013 S. Schweda, C. Spieler 29-Dec-2007 03:34 37** Extended /RESTORE qualifier to support timestamp restoration 38** options. 39** 02-012 Steven Schweda 07-Jul-2006 19:04 40** Added /TEXT=STMLF qualifier option. 41** 02-011 Christian Spieler 21-Apr-2005 01:23 42** Added /FULL=DIAGNOSTICS option modifier. 43** 02-010 Steven Schweda 14-FEB-2005 20:04 44** Added /DOT_VERSION (-Y) and /ODS2 (-2) qualifiers. 45** 02-009 Steven Schweda 28-JAN-2005 16:16 46** Added /TIMESTAMP (-T) qualifier. 47** 02-008 Christian Spieler 08-DEC-2001 23:44 48** Added support for /TRAVERSE_DIRS argument 49** 02-007 Christian Spieler 24-SEP-2001 21:12 50** Escape verbatim '%' chars in format strings; version unchanged. 51** 02-007 Onno van der Linden 02-Jul-1998 19:07 52** Modified to support GNU CC 2.8 on Alpha; version unchanged. 53** 02-007 Johnny Lee 25-Jun-1998 07:38 54** Fixed typo (superfluous ';'); no version num change. 55** 02-007 Hunter Goatley 11-NOV-1997 10:38 56** Fixed "zip" vs. "unzip" typo; no version num change. 57** 02-007 Christian Spieler 14-SEP-1997 22:43 58** Cosmetic mods to stay in sync with Zip; no version num change. 59** 02-007 Christian Spieler 12-JUL-1997 02:05 60** Revised argv vector construction for better handling of quoted 61** arguments (e.g.: embedded white space); no version num change. 62** 02-007 Christian Spieler 04-MAR-1997 22:25 63** Made /CASE_INSENSITIVE common to UnZip and ZipInfo mode; 64** added support for /PASSWORD="decryption_key" argument. 65** 02-006 Christian Spieler 11-MAY-1996 22:40 66** Added SFX version of VMSCLI_usage(). 67** 02-005 Patrick Ellis 09-MAY-1996 22:25 68** Show UNIX style usage screen when UNIX style options are used. 69** 02-004 Christian Spieler 06-FEB-1996 02:20 70** Added /HELP qualifier. 71** 02-003 Christian Spieler 23-DEC-1995 17:20 72** Adapted to UnZip 5.2. 73** 02-002 Hunter Goatley 16-JUL-1994 10:20 74** Fixed some typos. 75** 02-001 Cave Newt 14-JUL-1994 15:18 76** Removed obsolete /EXTRACT option; fixed /*TEXT options; 77** wrote VMSCLI usage() function 78** 02-000 Hunter Goatley 12-JUL-1994 00:00 79** Original UnZip version (v5.11). 80** 01-000 Hunter Goatley 30-JUL-1993 07:54 81** Original version (for Zip v1.9p1). 82** 83*/ 84 85/* Stand-alone test procedure: 86 * 87 * cc /define = TEST=1 [.vms]cmdline.c /include = [] /object = [.vms] 88 * set command /object = [.vms]unz_cli.obj [.vms]unz_cli.cld 89 * link /executable = [] [.vms]cmdline.obj, [.vms]unz_cli.obj 90 * EXEC*UTE == "$SYS$DISK:[]'" 91 * exec cmdline [ /qualifiers ...] [parameters ...] 92 */ 93 94 95 96/* 2004-12-13 SMS. 97 * Disabled the module name macro to accommodate old GNU C which didn't 98 * obey the directive, and thus confused MMS/MMK where the object 99 * library dependencies need to have the correct module name. 100 */ 101#if 0 102#if defined(__DECC) || defined(__GNUC__) 103#pragma module module_name module_ident 104#else 105#module module_name module_ident 106#endif 107#endif /* 0 */ 108 109#define UNZIP_INTERNAL 110#include "unzip.h" 111#ifndef TEST 112# include "unzvers.h" /* for VMSCLI_usage() */ 113#endif /* !TEST */ 114 115/* Workaround for broken header files of older DECC distributions 116 * that are incompatible with the /NAMES=AS_IS qualifier. */ 117/* - lib$routines.h definitions: */ 118#define lib$establish LIB$ESTABLISH 119#define lib$get_foreign LIB$GET_FOREIGN 120#define lib$get_input LIB$GET_INPUT 121#define lib$sig_to_ret LIB$SIG_TO_RET 122/* - str$routines.h definitions: */ 123#define str$concat STR$CONCAT 124#define str$find_first_substring STR$FIND_FIRST_SUBSTRING 125 126#include <ssdef.h> 127#include <descrip.h> 128#include <climsgdef.h> 129#include <clidef.h> 130#include <lib$routines.h> 131#include <str$routines.h> 132 133#ifndef CLI$_COMMA 134globalvalue CLI$_COMMA; 135#endif 136 137/* 138** "Macro" to initialize a dynamic string descriptor. 139*/ 140#define init_dyndesc(dsc) {\ 141 dsc.dsc$w_length = 0;\ 142 dsc.dsc$b_dtype = DSC$K_DTYPE_T;\ 143 dsc.dsc$b_class = DSC$K_CLASS_D;\ 144 dsc.dsc$a_pointer = NULL;} 145 146/* 147** Memory allocation step for argv string buffer. 148*/ 149#define ARGBSIZE_UNIT 256 150 151/* 152** Memory reallocation macro for argv string buffer. 153*/ 154#define CHECK_BUFFER_ALLOCATION(buf, reserved, requested) { \ 155 if ((requested) > (reserved)) { \ 156 char *save_buf = (buf); \ 157 (reserved) += ARGBSIZE_UNIT; \ 158 if (((buf) = (char *) realloc((buf), (reserved))) == NULL) { \ 159 if (save_buf != NULL) free(save_buf); \ 160 return (SS$_INSFMEM); \ 161 } \ 162 } \ 163} 164 165/* 166** Define descriptors for all of the CLI parameters and qualifiers. 167*/ 168#if 0 169$DESCRIPTOR(cli_extract, "EXTRACT"); /* obsolete */ 170#endif 171$DESCRIPTOR(cli_text, "TEXT"); /* -a[a] */ 172$DESCRIPTOR(cli_text_auto, "TEXT.AUTO"); /* -a */ 173$DESCRIPTOR(cli_text_all, "TEXT.ALL"); /* -aa */ 174$DESCRIPTOR(cli_text_none, "TEXT.NONE"); /* ---a */ 175$DESCRIPTOR(cli_text_stmlf, "TEXT.STMLF"); /* -S */ 176$DESCRIPTOR(cli_binary, "BINARY"); /* -b[b] */ 177$DESCRIPTOR(cli_binary_auto, "BINARY.AUTO"); /* -b */ 178$DESCRIPTOR(cli_binary_all, "BINARY.ALL"); /* -bb */ 179$DESCRIPTOR(cli_binary_none, "BINARY.NONE"); /* ---b */ 180$DESCRIPTOR(cli_case_insensitive,"CASE_INSENSITIVE"); /* -C */ 181$DESCRIPTOR(cli_screen, "SCREEN"); /* -c */ 182$DESCRIPTOR(cli_directory, "DIRECTORY"); /* -d */ 183$DESCRIPTOR(cli_freshen, "FRESHEN"); /* -f */ 184$DESCRIPTOR(cli_help, "HELP"); /* -h */ 185$DESCRIPTOR(cli_junk, "JUNK"); /* -j */ 186$DESCRIPTOR(cli_lowercase, "LOWERCASE"); /* -L */ 187$DESCRIPTOR(cli_list, "LIST"); /* -l */ 188$DESCRIPTOR(cli_brief, "BRIEF"); /* -l */ 189$DESCRIPTOR(cli_full, "FULL"); /* -v */ 190$DESCRIPTOR(cli_full_diags, "FULL.DIAGNOSTICS"); /* -vv */ 191$DESCRIPTOR(cli_existing, "EXISTING"); /* -o, -oo, -n */ 192$DESCRIPTOR(cli_exist_newver, "EXISTING.NEW_VERSION"); /* -o */ 193$DESCRIPTOR(cli_exist_over, "EXISTING.OVERWRITE"); /* -oo */ 194$DESCRIPTOR(cli_exist_noext, "EXISTING.NOEXTRACT"); /* -n */ 195$DESCRIPTOR(cli_overwrite, "OVERWRITE"); /* -o, -n */ 196$DESCRIPTOR(cli_quiet, "QUIET"); /* -q */ 197$DESCRIPTOR(cli_super_quiet, "QUIET.SUPER"); /* -qq */ 198$DESCRIPTOR(cli_test, "TEST"); /* -t */ 199$DESCRIPTOR(cli_pipe, "PIPE"); /* -p */ 200$DESCRIPTOR(cli_password, "PASSWORD"); /* -P */ 201$DESCRIPTOR(cli_timestamp, "TIMESTAMP"); /* -T */ 202$DESCRIPTOR(cli_uppercase, "UPPERCASE"); /* -U */ 203$DESCRIPTOR(cli_update, "UPDATE"); /* -u */ 204$DESCRIPTOR(cli_version, "VERSION"); /* -V */ 205$DESCRIPTOR(cli_restore, "RESTORE"); /* -X */ 206$DESCRIPTOR(cli_restore_own, "RESTORE.OWNER_PROT"); /* -X */ 207$DESCRIPTOR(cli_restore_date, "RESTORE.DATE"); /* -DD */ 208$DESCRIPTOR(cli_restore_date_all, "RESTORE.DATE.ALL"); /* --D */ 209$DESCRIPTOR(cli_restore_date_files, "RESTORE.DATE.FILES"); /* -D */ 210$DESCRIPTOR(cli_dot_version, "DOT_VERSION"); /* -Y */ 211$DESCRIPTOR(cli_comment, "COMMENT"); /* -z */ 212$DESCRIPTOR(cli_exclude, "EXCLUDE"); /* -x */ 213$DESCRIPTOR(cli_ods2, "ODS2"); /* -2 */ 214$DESCRIPTOR(cli_traverse, "TRAVERSE_DIRS"); /* -: */ 215 216$DESCRIPTOR(cli_information, "ZIPINFO"); /* -Z */ 217$DESCRIPTOR(cli_short, "SHORT"); /* -Zs */ 218$DESCRIPTOR(cli_medium, "MEDIUM"); /* -Zm */ 219$DESCRIPTOR(cli_long, "LONG"); /* -Zl */ 220$DESCRIPTOR(cli_verbose, "VERBOSE"); /* -Zv */ 221$DESCRIPTOR(cli_header, "HEADER"); /* -Zh */ 222$DESCRIPTOR(cli_totals, "TOTALS"); /* -Zt */ 223$DESCRIPTOR(cli_times, "TIMES"); /* -ZT */ 224$DESCRIPTOR(cli_one_line, "ONE_LINE"); /* -Z2 */ 225 226$DESCRIPTOR(cli_page, "PAGE"); /* -M , -ZM */ 227 228$DESCRIPTOR(cli_yyz, "YYZ_UNZIP"); 229 230$DESCRIPTOR(cli_zipfile, "ZIPFILE"); 231$DESCRIPTOR(cli_infile, "INFILE"); 232$DESCRIPTOR(unzip_command, "unzip "); 233 234static int show_VMSCLI_usage; 235 236#ifndef vms_unzip_cld 237# define vms_unzip_cld VMS_UNZIP_CLD 238#endif 239#if defined(__DECC) || defined(__GNUC__) 240extern void *vms_unzip_cld; 241#else 242globalref void *vms_unzip_cld; 243#endif 244 245/* extern unsigned long LIB$GET_INPUT(void), LIB$SIG_TO_RET(void); */ 246 247#ifndef cli$dcl_parse 248# define cli$dcl_parse CLI$DCL_PARSE 249#endif 250#ifndef cli$present 251# define cli$present CLI$PRESENT 252#endif 253#ifndef cli$get_value 254# define cli$get_value CLI$GET_VALUE 255#endif 256extern unsigned long cli$dcl_parse (); 257extern unsigned long cli$present (); 258extern unsigned long cli$get_value (); 259 260unsigned long vms_unzip_cmdline (int *, char ***); 261static unsigned long get_list (struct dsc$descriptor_s *, 262 struct dsc$descriptor_d *, int, 263 char **, unsigned long *, unsigned long *); 264static unsigned long check_cli (struct dsc$descriptor_s *); 265 266 267#ifdef TEST 268int 269main(int argc, char **argv) 270{ 271 return (vms_unzip_cmdline(&argc, &argv)); 272} 273#endif /* TEST */ 274 275 276unsigned long 277vms_unzip_cmdline (int *argc_p, char ***argv_p) 278{ 279/* 280** Routine: vms_unzip_cmdline 281** 282** Function: 283** 284** Parse the DCL command line and create a fake argv array to be 285** handed off to Zip. 286** 287** NOTE: the argv[] is built as we go, so all the parameters are 288** checked in the appropriate order!! 289** 290** Formal parameters: 291** 292** argc_p - Address of int to receive the new argc 293** argv_p - Address of char ** to receive the argv address 294** 295** Calling sequence: 296** 297** status = vms_unzip_cmdline (&argc, &argv); 298** 299** Returns: 300** 301** SS$_NORMAL - Success. 302** SS$_INSFMEM - A malloc() or realloc() failed 303** SS$_ABORT - Bad time value 304** 305*/ 306 register unsigned long status; 307 char options[256]; 308 char *the_cmd_line; /* buffer for argv strings */ 309 unsigned long cmdl_size; /* allocated size of buffer */ 310 unsigned long cmdl_len; /* used size of buffer */ 311 char *ptr; 312 int x, len, zipinfo, exclude_list; 313 int restore_date; 314 315 int new_argc; 316 char **new_argv; 317 318 struct dsc$descriptor_d work_str; 319 struct dsc$descriptor_d foreign_cmdline; 320 struct dsc$descriptor_d output_directory; 321 struct dsc$descriptor_d password_arg; 322 323 init_dyndesc(work_str); 324 init_dyndesc(foreign_cmdline); 325 init_dyndesc(output_directory); 326 init_dyndesc(password_arg); 327 328 /* 329 ** See if the program was invoked by the CLI (SET COMMAND) or by 330 ** a foreign command definition. Check for /YYZ_UNZIP, which is a 331 ** valid default qualifier solely for this test. 332 */ 333 show_VMSCLI_usage = TRUE; 334 status = check_cli(&cli_yyz); 335 if (!(status & 1)) { 336 lib$get_foreign(&foreign_cmdline); 337 /* 338 ** If nothing was returned or the first character is a "-", then 339 ** assume it's a UNIX-style command and return. 340 */ 341 if (foreign_cmdline.dsc$w_length == 0) 342 return (SS$_NORMAL); 343 if ((*(foreign_cmdline.dsc$a_pointer) == '-') || 344 ((foreign_cmdline.dsc$w_length > 1) && 345 (*(foreign_cmdline.dsc$a_pointer) == '"') && 346 (*(foreign_cmdline.dsc$a_pointer + 1) == '-'))) { 347 show_VMSCLI_usage = FALSE; 348 return (SS$_NORMAL); 349 } 350 351 str$concat(&work_str, &unzip_command, &foreign_cmdline); 352 status = cli$dcl_parse(&work_str, &vms_unzip_cld, lib$get_input, 353 lib$get_input, 0); 354 if (!(status & 1)) return (status); 355 } 356 357 /* 358 ** There's always going to be a new_argv[] because of the image name. 359 */ 360 if ((the_cmd_line = (char *) malloc(cmdl_size = ARGBSIZE_UNIT)) == NULL) 361 return (SS$_INSFMEM); 362 363 strcpy(the_cmd_line, "unzip"); 364 cmdl_len = sizeof("unzip"); 365 366 /* 367 ** First, check to see if any of the regular options were specified. 368 */ 369 370 options[0] = '-'; 371 ptr = &options[1]; /* Point to temporary buffer */ 372 373 /* 374 ** Is it ZipInfo?? 375 */ 376 zipinfo = 0; 377 status = cli$present(&cli_information); 378 if (status & 1) { 379 380 zipinfo = 1; 381 382 *ptr++ = 'Z'; 383 384 if (cli$present(&cli_one_line) & 1) 385 *ptr++ = '2'; 386 if (cli$present(&cli_short) & 1) 387 *ptr++ = 's'; 388 if (cli$present(&cli_medium) & 1) 389 *ptr++ = 'm'; 390 if (cli$present(&cli_long) & 1) 391 *ptr++ = 'l'; 392 if (cli$present(&cli_verbose) & 1) 393 *ptr++ = 'v'; 394 if (cli$present(&cli_header) & 1) 395 *ptr++ = 'h'; 396 if (cli$present(&cli_comment) & 1) 397 *ptr++ = 'c'; 398 if (cli$present(&cli_totals) & 1) 399 *ptr++ = 't'; 400 if (cli$present(&cli_times) & 1) 401 *ptr++ = 'T'; 402 403 } 404 else { 405 406#if 0 407 /* 408 ** Extract files? 409 */ 410 status = cli$present(&cli_extract); 411 if (status == CLI$_NEGATED) 412 *ptr++ = '-'; 413 if (status != CLI$_ABSENT) 414 *ptr++ = 'x'; 415#endif 416 417 /* 418 ** Write binary files in VMS binary (fixed-length, 512-byte records, 419 ** record attributes: none) format 420 ** (auto-convert, or force to convert all files) 421 */ 422 status = cli$present(&cli_binary); 423 if (status != CLI$_ABSENT) { 424 *ptr++ = '-'; 425 *ptr++ = '-'; 426 *ptr++ = 'b'; 427 if ((status & 1) && 428 !((status = cli$present(&cli_binary_none)) & 1)) { 429 *ptr++ = 'b'; 430 if ((status = cli$present(&cli_binary_all)) & 1) 431 *ptr++ = 'b'; 432 } 433 } 434 435 /* 436 ** Convert files as text (CR LF -> LF, etc.) 437 ** (auto-convert, or force to convert all files) 438 */ 439 status = cli$present(&cli_text); 440 if (status != CLI$_ABSENT) { 441 *ptr++ = '-'; 442 *ptr++ = '-'; 443 *ptr++ = 'a'; 444 if ((status & 1) && 445 !((status = cli$present(&cli_text_none)) & 1)) { 446 *ptr++ = 'a'; 447 if ((status = cli$present(&cli_text_all)) & 1) 448 *ptr++ = 'a'; 449 if ((status = cli$present(&cli_text_stmlf)) & 1) 450 *ptr++ = 'S'; 451 } 452 } 453 454 /* 455 ** Extract files to screen? 456 */ 457 status = cli$present(&cli_screen); 458 if (status == CLI$_NEGATED) 459 *ptr++ = '-'; 460 if (status != CLI$_ABSENT) 461 *ptr++ = 'c'; 462 463 /* 464 ** Re-create directory structure? (default) 465 */ 466 status = cli$present(&cli_directory); 467 if (status == CLI$_PRESENT) { 468 status = cli$get_value(&cli_directory, &output_directory); 469 } 470 471 /* 472 ** Restore directory date-times. 473 */ 474 restore_date = 0; 475 status = cli$present(&cli_restore_date); 476 if (status != CLI$_ABSENT) { 477 /* Emit "----D" to reset the timestamp restore state "D_flag" 478 ** consistently to 0 (independent of optional environment 479 ** option settings). 480 */ 481 *ptr++ = '-'; 482 *ptr++ = '-'; 483 *ptr++ = '-'; 484 *ptr++ = 'D'; 485 if (status == CLI$_NEGATED) { 486 /* /RESTORE=NODATE */ 487 restore_date = 2; 488 } else { 489 status = cli$present(&cli_restore_date_all); 490 if (status == CLI$_PRESENT) { 491 /* /RESTORE=(DATE=ALL) */ 492 restore_date = 0; 493 } else { 494 /* /RESTORE=(DATE=FILES) (default) */ 495 restore_date = 1; 496 } 497 } 498 /* Emit the required number of (positive) "D" characters. */ 499 while (restore_date > 0) { 500 *ptr++ = 'D'; 501 restore_date--; 502 } 503 } 504 505 /* 506 ** Freshen existing files, create none 507 */ 508 status = cli$present(&cli_freshen); 509 if (status == CLI$_NEGATED) 510 *ptr++ = '-'; 511 if (status != CLI$_ABSENT) 512 *ptr++ = 'f'; 513 514 /* 515 ** Show the help. 516 */ 517 status = cli$present(&cli_help); 518 if (status & 1) 519 *ptr++ = 'h'; 520 521 /* 522 ** Junk stored directory names on unzip 523 */ 524 status = cli$present(&cli_junk); 525 if (status == CLI$_NEGATED) 526 *ptr++ = '-'; 527 if (status != CLI$_ABSENT) 528 *ptr++ = 'j'; 529 530 /* 531 ** List contents (/BRIEF (default) or /FULL) 532 */ 533 status = cli$present(&cli_list); 534 if (status & 1) { 535 if (cli$present(&cli_full) & 1) { 536 *ptr++ = 'v'; 537 if (cli$present(&cli_full_diags) & 1) 538 *ptr++ = 'v'; 539 } else 540 *ptr++ = 'l'; 541 } 542 543 /* 544 ** Existing files: new version, overwrite, no extract? 545 */ 546 status = cli$present(&cli_exist_newver); 547 if (status == CLI$_PRESENT) { 548 *ptr++ = 'o'; 549 } 550 status = cli$present(&cli_exist_over); 551 if (status == CLI$_PRESENT) { 552 *ptr++ = 'o'; 553 *ptr++ = 'o'; 554 } 555 status = cli$present(&cli_exist_noext); 556 if (status == CLI$_PRESENT) { 557 *ptr++ = 'n'; 558 } 559 560 /* 561 ** Overwrite files (deprecated) ? 562 */ 563 status = cli$present(&cli_overwrite); 564 if (status == CLI$_NEGATED) 565 *ptr++ = 'n'; 566 else if (status != CLI$_ABSENT) 567 *ptr++ = 'o'; 568 569 /* 570 ** Decryption password from command line? 571 */ 572 status = cli$present(&cli_password); 573 if (status == CLI$_PRESENT) { 574 status = cli$get_value(&cli_password, &password_arg); 575 } 576 577 /* 578 ** Pipe files to SYS$OUTPUT with no informationals? 579 */ 580 status = cli$present(&cli_pipe); 581 if (status != CLI$_ABSENT) 582 *ptr++ = 'p'; 583 584 /* 585 ** Quiet 586 */ 587 status = cli$present(&cli_quiet); 588 if (status & 1) { 589 *ptr++ = 'q'; 590 if ((status = cli$present(&cli_super_quiet)) & 1) 591 *ptr++ = 'q'; 592 } 593 594 /* 595 ** Test archive integrity 596 */ 597 status = cli$present(&cli_test); 598 if (status == CLI$_NEGATED) 599 *ptr++ = '-'; 600 if (status != CLI$_ABSENT) 601 *ptr++ = 't'; 602 603 /* 604 ** Set archive timestamp according to its newest file. 605 */ 606 status = cli$present(&cli_timestamp); 607 if (status & 1) 608 *ptr++ = 'T'; 609 610 /* 611 ** Extract "foo.ext.###" as "foo.ext;###" (treat .### as version number) 612 */ 613 status = cli$present(&cli_dot_version); 614 if (status == CLI$_NEGATED) 615 *ptr++ = '-'; 616 if (status != CLI$_ABSENT) 617 *ptr++ = 'Y'; 618 619 /* 620 ** Force conversion of extracted file names to old ODS2 conventions 621 */ 622 status = cli$present(&cli_ods2); 623 if (status == CLI$_NEGATED) 624 *ptr++ = '-'; 625 if (status != CLI$_ABSENT) 626 *ptr++ = '2'; 627 628 /* 629 ** Traverse directories (don't skip "../" path components) 630 */ 631 status = cli$present(&cli_traverse); 632 if (status == CLI$_NEGATED) 633 *ptr++ = '-'; 634 if (status != CLI$_ABSENT) 635 *ptr++ = ':'; 636 637 /* 638 ** Make (some) names lowercase 639 */ 640 status = cli$present(&cli_lowercase); 641 if (status == CLI$_NEGATED) 642 *ptr++ = '-'; 643 if (status != CLI$_ABSENT) 644 *ptr++ = 'L'; 645 646 /* 647 ** Uppercase (don't convert to lower) 648 */ 649 status = cli$present(&cli_uppercase); 650 if (status == CLI$_NEGATED) 651 *ptr++ = '-'; 652 if (status != CLI$_ABSENT) 653 *ptr++ = 'U'; 654 655 /* 656 ** Update (extract only new and newer files) 657 */ 658 status = cli$present(&cli_update); 659 if (status == CLI$_NEGATED) 660 *ptr++ = '-'; 661 if (status != CLI$_ABSENT) 662 *ptr++ = 'u'; 663 664 /* 665 ** Version (retain VMS/DEC-20 file versions) 666 */ 667 status = cli$present(&cli_version); 668 if (status == CLI$_NEGATED) 669 *ptr++ = '-'; 670 if (status != CLI$_ABSENT) 671 *ptr++ = 'V'; 672 673 /* 674 ** Restore owner/protection info 675 */ 676 status = cli$present(&cli_restore_own); 677 if (status != CLI$_ABSENT) { 678 if (status == CLI$_NEGATED) { 679 *ptr++ = '-'; 680 } else if ((status = cli$present(&cli_restore)) 681 == CLI$_NEGATED) { 682 *ptr++ = '-'; 683 } 684 *ptr++ = 'X'; 685 } 686 687 /* 688 ** Display only the archive comment 689 */ 690 status = cli$present(&cli_comment); 691 if (status == CLI$_NEGATED) 692 *ptr++ = '-'; 693 if (status != CLI$_ABSENT) 694 *ptr++ = 'z'; 695 696 } /* ZipInfo check way up there.... */ 697 698 /* The following options are common to both UnZip and ZipInfo mode. */ 699 700 /* 701 ** Match filenames case-insensitively (-C) 702 */ 703 status = cli$present(&cli_case_insensitive); 704 if (status == CLI$_NEGATED) 705 *ptr++ = '-'; 706 if (status != CLI$_ABSENT) 707 *ptr++ = 'C'; 708 709 /* 710 ** Use builtin pager for all screen output 711 */ 712 status = cli$present(&cli_page); 713 if (status == CLI$_NEGATED) 714 *ptr++ = '-'; 715 if (status != CLI$_ABSENT) 716 *ptr++ = 'M'; 717 718 /* 719 ** Check existence of a list of files to exclude, fetch is done later. 720 */ 721 status = cli$present(&cli_exclude); 722 exclude_list = ((status & 1) != 0); 723 724 /* 725 ** If the user didn't give any DCL qualifier, assume he wants the 726 ** Un*x interface. 727 if ( (ptr == &options[1]) && 728 (output_directory.dsc$w_length == 0) && 729 (password_arg.dsc$w_length == 0) && 730 (!exclude_list) ) { 731 free(the_cmd_line); 732 return (SS$_NORMAL); 733 } 734 */ 735 736 /* 737 ** Now copy the final options string to the_cmd_line. 738 */ 739 len = ptr - &options[0]; 740 if (len > 1) { 741 options[len] = '\0'; 742 x = cmdl_len; 743 cmdl_len += len + 1; 744 CHECK_BUFFER_ALLOCATION(the_cmd_line, cmdl_size, cmdl_len) 745 strcpy(&the_cmd_line[x], options); 746 } 747 748 /* 749 ** If specified, add the decryption password argument. 750 **/ 751 if (password_arg.dsc$w_length != 0) { 752 x = cmdl_len; 753 cmdl_len += password_arg.dsc$w_length + 4; 754 CHECK_BUFFER_ALLOCATION(the_cmd_line, cmdl_size, cmdl_len) 755 strcpy(&the_cmd_line[x], "-P"); 756 strncpy(&the_cmd_line[x+3], password_arg.dsc$a_pointer, 757 password_arg.dsc$w_length); 758 the_cmd_line[cmdl_len-1] = '\0'; 759 } 760 761 /* 762 ** Now get the specified zip file name. 763 */ 764 status = cli$present(&cli_zipfile); 765 if (status & 1) { 766 status = cli$get_value(&cli_zipfile, &work_str); 767 768 x = cmdl_len; 769 cmdl_len += work_str.dsc$w_length + 1; 770 CHECK_BUFFER_ALLOCATION(the_cmd_line, cmdl_size, cmdl_len) 771 strncpy(&the_cmd_line[x], work_str.dsc$a_pointer, 772 work_str.dsc$w_length); 773 the_cmd_line[cmdl_len-1] = '\0'; 774 775 } 776 777 /* 778 ** Get the output directory, for UnZip. 779 **/ 780 if (output_directory.dsc$w_length != 0) { 781 x = cmdl_len; 782 cmdl_len += output_directory.dsc$w_length + 4; 783 CHECK_BUFFER_ALLOCATION(the_cmd_line, cmdl_size, cmdl_len) 784 strcpy(&the_cmd_line[x], "-d"); 785 strncpy(&the_cmd_line[x+3], output_directory.dsc$a_pointer, 786 output_directory.dsc$w_length); 787 the_cmd_line[cmdl_len-1] = '\0'; 788 } 789 790 /* 791 ** Run through the list of files to unzip. 792 */ 793 status = cli$present(&cli_infile); 794 if (status & 1) { 795 status = get_list(&cli_infile, &foreign_cmdline, '\0', 796 &the_cmd_line, &cmdl_size, &cmdl_len); 797 if (!(status & 1)) return (status); 798 } 799 800 /* 801 ** Get the list of files to exclude, if there are any. 802 */ 803 if (exclude_list) { 804 x = cmdl_len; 805 cmdl_len += 3; 806 CHECK_BUFFER_ALLOCATION(the_cmd_line, cmdl_size, cmdl_len) 807 strcpy(&the_cmd_line[x], "-x"); 808 809 status = get_list(&cli_exclude, &foreign_cmdline, '\0', 810 &the_cmd_line, &cmdl_size, &cmdl_len); 811 if (!(status & 1)) return (status); 812 } 813 814 /* 815 ** We have finished collecting the strings for the argv vector, 816 ** release unused space. 817 */ 818 if ((the_cmd_line = (char *) realloc(the_cmd_line, cmdl_len)) == NULL) 819 return (SS$_INSFMEM); 820 821 /* 822 ** Now that we've built our new UNIX-like command line, count the 823 ** number of args and build an argv array. 824 */ 825 for (new_argc = 0, x = 0; x < cmdl_len; x++) 826 if (the_cmd_line[x] == '\0') 827 new_argc++; 828 829 /* 830 ** Allocate memory for the new argv[]. The last element of argv[] 831 ** is supposed to be NULL, so allocate enough for new_argc+1. 832 */ 833 if ((new_argv = (char **) calloc(new_argc+1, sizeof(char *))) == NULL) 834 return (SS$_INSFMEM); 835 836 /* 837 ** For each option, store the address in new_argv[] and convert the 838 ** separating blanks to nulls so each argv[] string is terminated. 839 */ 840 for (ptr = the_cmd_line, x = 0; x < new_argc; x++) { 841 new_argv[x] = ptr; 842 ptr += strlen(ptr) + 1; 843 } 844 new_argv[new_argc] = NULL; 845 846#if defined(TEST) || defined(DEBUG) 847 printf("new_argc = %d\n", new_argc); 848 for (x = 0; x < new_argc; x++) 849 printf("new_argv[%d] = %s\n", x, new_argv[x]); 850#endif /* TEST || DEBUG */ 851 852 /* 853 ** All finished. Return the new argc and argv[] addresses to Zip. 854 */ 855 *argc_p = new_argc; 856 *argv_p = new_argv; 857 858 return (SS$_NORMAL); 859} 860 861 862 863static unsigned long 864get_list (struct dsc$descriptor_s *qual, struct dsc$descriptor_d *rawtail, 865 int delim, char **p_str, unsigned long *p_size, unsigned long *p_end) 866{ 867/* 868** Routine: get_list 869** 870** Function: This routine runs through a comma-separated CLI list 871** and copies the strings to the argv buffer. The 872** specified separation character is used to separate 873** the strings in the argv buffer. 874** 875** All unquoted strings are converted to lower-case. 876** 877** Formal parameters: 878** 879** qual - Address of descriptor for the qualifier name 880** rawtail - Address of descriptor for the full command line tail 881** delim - Character to use to separate the list items 882** p_str - Address of pointer pointing to output buffer (argv strings) 883** p_size - Address of number containing allocated size for output string 884** p_end - Address of number containing used length in output buf 885** 886*/ 887 888 register unsigned long status; 889 struct dsc$descriptor_d work_str; 890 891 init_dyndesc(work_str); 892 893 status = cli$present(qual); 894 if (status & 1) { 895 896 unsigned long len, old_len; 897 long ind, sind; 898 int keep_case; 899 char *src, *dst; int x; 900 901 /* 902 ** Just in case the string doesn't exist yet, though it does. 903 */ 904 if (*p_str == NULL) { 905 *p_size = ARGBSIZE_UNIT; 906 if ((*p_str = (char *) malloc(*p_size)) == NULL) 907 return (SS$_INSFMEM); 908 len = 0; 909 } else { 910 len = *p_end; 911 } 912 913 while ((status = cli$get_value(qual, &work_str)) & 1) { 914 old_len = len; 915 len += work_str.dsc$w_length + 1; 916 CHECK_BUFFER_ALLOCATION(*p_str, *p_size, len) 917 918 /* 919 ** Look for the filename in the original foreign command 920 ** line to see if it was originally quoted. If so, then 921 ** don't convert it to lowercase. 922 */ 923 keep_case = FALSE; 924 str$find_first_substring(rawtail, &ind, &sind, &work_str); 925 if ((ind > 1 && *(rawtail->dsc$a_pointer + ind - 2) == '"') || 926 (ind == 0)) 927 keep_case = TRUE; 928 929 /* 930 ** Copy the string to the buffer, converting to lowercase. 931 */ 932 src = work_str.dsc$a_pointer; 933 dst = *p_str+old_len; 934 for (x = 0; x < work_str.dsc$w_length; x++) { 935 if (!keep_case && ((*src >= 'A') && (*src <= 'Z'))) 936 *dst++ = *src++ + 32; 937 else 938 *dst++ = *src++; 939 } 940 if (status == CLI$_COMMA) 941 (*p_str)[len-1] = (char)delim; 942 else 943 (*p_str)[len-1] = '\0'; 944 } 945 *p_end = len; 946 } 947 948 return (SS$_NORMAL); 949 950} 951 952 953static unsigned long 954check_cli (struct dsc$descriptor_s *qual) 955{ 956/* 957** Routine: check_cli 958** 959** Function: Check to see if a CLD was used to invoke the program. 960** 961** Formal parameters: 962** 963** qual - Address of descriptor for qualifier name to check. 964** 965*/ 966 lib$establish(lib$sig_to_ret); /* Establish condition handler */ 967 return (cli$present(qual)); /* Just see if something was given */ 968} 969 970 971#ifndef TEST 972#ifdef SFX 973 974#ifdef SFX_EXDIR 975# define SFXOPT_EXDIR "\n and /DIRECTORY=exdir-spec" 976#else 977# define SFXOPT_EXDIR "" 978#endif 979 980#ifdef MORE 981# define SFXOPT1 "/PAGE, " 982#else 983# define SFXOPT1 "" 984#endif 985 986int VMSCLI_usage(__GPRO__ int error) /* returns PK-type error code */ 987{ 988 extern ZCONST char UnzipSFXBanner[]; 989#ifdef BETA 990 extern ZCONST char BetaVersion[]; 991#endif 992 int flag; 993 994 if (!show_VMSCLI_usage) 995 return usage(__G__ error); 996 997 flag = (error? 1 : 0); 998 999 Info(slide, flag, ((char *)slide, UnzipSFXBanner, 1000 UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL, UZ_VERSION_DATE)); 1001 Info(slide, flag, ((char *)slide, "\ 1002Valid main options are /TEST, /FRESHEN, /UPDATE, /PIPE, /SCREEN, /COMMENT%s.\n", 1003 SFXOPT_EXDIR)); 1004 Info(slide, flag, ((char *)slide, "\ 1005Modifying options are /TEXT, /BINARY, /JUNK, /EXISTING, /QUIET,\n\ 1006 /CASE_INSENSITIVE, /LOWERCASE, %s/VERSION, /RESTORE.\n", 1007 SFXOPT1)); 1008#ifdef BETA 1009 Info(slide, flag, ((char *)slide, BetaVersion, "\n", "SFX")); 1010#endif 1011 1012 if (error) 1013 return PK_PARAM; 1014 else 1015 return PK_COOL; /* just wanted usage screen: no error */ 1016 1017} /* end function VMSCLI_usage() */ 1018 1019 1020#else /* !SFX */ 1021 1022int VMSCLI_usage(__GPRO__ int error) /* returns PK-type error code */ 1023{ 1024 extern ZCONST char UnzipUsageLine1[]; 1025#ifdef BETA 1026 extern ZCONST char BetaVersion[]; 1027#endif 1028 int flag; 1029 1030 if (!show_VMSCLI_usage) 1031 return usage(__G__ error); 1032 1033/*--------------------------------------------------------------------------- 1034 If user requested usage, send it to stdout; else send to stderr. 1035 ---------------------------------------------------------------------------*/ 1036 1037 flag = (error? 1 : 0); 1038 1039 1040/*--------------------------------------------------------------------------- 1041 Print either ZipInfo usage or UnZip usage, depending on incantation. 1042 ---------------------------------------------------------------------------*/ 1043 1044 if (uO.zipinfo_mode) { 1045 1046#ifndef NO_ZIPINFO 1047 1048 Info(slide, flag, ((char *)slide, "\ 1049ZipInfo %d.%d%d%s %s, by Newtware and the fine folks at Info-ZIP.\n\n\ 1050List name, date/time, attribute, size, compression method, etc., about files\n\ 1051in list (excluding those in xlist) contained in the specified .zip archive(s).\ 1052\n\"file[.zip]\" may be a wildcard name containing * or %% (e.g., \"*font-%%\ 1053.zip\").\n", ZI_MAJORVER, ZI_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL, 1054 UZ_VERSION_DATE)); 1055 1056 Info(slide, flag, ((char *)slide, "\ 1057 usage: zipinfo file[.zip] [list] [/EXCL=(xlist)] [/DIR=exdir] /options\n\ 1058 or: unzip /ZIPINFO file[.zip] [list] [/EXCL=(xlist)] [/DIR=exdir] /options\ 1059\n\nmain\ 1060 listing-format options: /SHORT short \"ls -l\" format (def.)\n\ 1061 /ONE_LINE just filenames, one/line /MEDIUM medium Unix \"ls -l\" format\n\ 1062 /VERBOSE verbose, multi-page format /LONG long Unix \"ls -l\" format\n\ 1063")); 1064 1065 Info(slide, flag, ((char *)slide, "\ 1066miscellaneous options:\n \ 1067/HEADER print header line /TOTALS totals for listed files or for all\n\ 1068 /COMMENT print zipfile comment /TIMES times in sortable decimal format\n\ 1069 /[NO]CASE_INSENSITIVE match filenames case-insensitively\n\ 1070 /[NO]PAGE page output through built-in \"more\"\n\ 1071 /EXCLUDE=(file-spec1,etc.) exclude file-specs from listing\n")); 1072 1073 Info(slide, flag, ((char *)slide, "\n\ 1074Type unzip \"-Z\" for Unix style flags\n\ 1075Remember that non-lowercase filespecs must be\ 1076 quoted in VMS (e.g., \"Makefile\").\n")); 1077 1078#endif /* !NO_ZIPINFO */ 1079 1080 } else { /* UnZip mode */ 1081 1082 Info(slide, flag, ((char *)slide, UnzipUsageLine1, 1083 UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL, 1084 UZ_VERSION_DATE)); 1085 1086#ifdef BETA 1087 Info(slide, flag, ((char *)slide, BetaVersion, "", "")); 1088#endif 1089 1090 Info(slide, flag, ((char *)slide, "\ 1091Usage: unzip file[.zip] [list] [/EXCL=(xlist)] [/DIR=exdir] /options /modifiers\ 1092\n Default action is to extract files in list, except those in xlist, to exdir\ 1093;\n file[.zip] may be a wildcard. %s\n\n", 1094#ifdef NO_ZIPINFO 1095 "(ZipInfo mode is disabled in this version.)" 1096#else 1097 "Type \"unzip /ZIPINFO\" for ZipInfo-mode usage." 1098#endif 1099 )); 1100 1101 Info(slide, flag, ((char *)slide, "\ 1102Major options include (type unzip -h for Unix style flags):\n\ 1103 /[NO]TEST, /LIST, /[NO]SCREEN, /PIPE, /[NO]FRESHEN, /[NO]UPDATE,\n\ 1104 /[NO]COMMENT, /DIRECTORY=directory-spec, /EXCLUDE=(file-spec1,etc.)\n\n\ 1105Modifiers include:\n\ 1106 /BRIEF, /FULL, /[NO]TEXT[=NONE|AUTO|ALL], /[NO]BINARY[=NONE|AUTO|ALL],\n\ 1107 /EXISTING={NEW_VERSION|OVERWRITE|NOEXTRACT}, /[NO]JUNK, /QUIET,\n\ 1108 /QUIET[=SUPER], /[NO]PAGE, /[NO]CASE_INSENSITIVE, /[NO]LOWERCASE,\n\ 1109 /[NO]VERSION, /RESTORE[=([NO]OWNER_PROT[,NODATE|DATE={ALL|FILES}])]\n\n")); 1110 1111 Info(slide, flag, ((char *)slide, "\ 1112Examples (see unzip.txt or \"HELP UNZIP\" for more info):\n\ 1113 unzip edit1 /EXCL=joe.jou /CASE_INSENSITIVE => Extract all files except\ 1114\n\ 1115 joe.jou (or JOE.JOU, or any combination of case) from zipfile edit1.zip.\ 1116\n \ 1117 unzip zip201 \"Makefile.VMS\" vms/*.[ch] => extract VMS Makefile and\ 1118\n\ 1119 *.c and *.h files; must quote uppercase names if /CASE_INSENS not used.\ 1120\n\ 1121 unzip foo /DIR=tmp:[.test] /JUNK /TEXT /EXIS=NEW => extract all files to\ 1122\n\ 1123 tmp. dir., flatten hierarchy, auto-conv. text files, create new versions.\ 1124\n")); 1125 1126 } /* end if (zipinfo_mode) */ 1127 1128 if (error) 1129 return PK_PARAM; 1130 else 1131 return PK_COOL; /* just wanted usage screen: no error */ 1132 1133} /* end function VMSCLI_usage() */ 1134 1135#endif /* ?SFX */ 1136#endif /* !TEST */ 1137