Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

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