home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc190.zip / ckvfio.c < prev    next >
C/C++ Source or Header  |  1994-11-12  |  104KB  |  3,205 lines

  1. #ifndef VMS
  2.       ERROR -- CKVFIO.C is used only on the OpenVMS(tm) Operating System
  3. #endif /* VMS */
  4.  
  5. #ifdef __ALPHA
  6. # define CKVFIO_OS_ARCH_STRING " OpenVMS(tm) AXP(tm)";
  7.          /* do nothing */
  8. #else
  9. # ifdef VAX
  10. #  module ckvfio "2.0-116"
  11. #  define CKVFIO_OS_ARCH_STRING " OpenVMS(tm) VAX(tm)";
  12. # else
  13. #  ifdef __GNUC__
  14. #     define CKVFIO_OS_ARCH_STRING " OpenVMS(tm) VAX(tm) (GCC)";
  15. #  else
  16. #     ERROR -- CKVTIO.C unknown architecture, neither VAX(tm) nor AXP(tm)
  17. #  endif /* __GNUC__ */
  18. # endif /* VAX */
  19. #endif /* __ALPHA */
  20.  
  21. char *ckzv = "File support, 2.0(117), 4 Oct 94";
  22. char *ckzsys = CKVFIO_OS_ARCH_STRING;
  23.  
  24. /* lt. 1992-10-08 End
  25.  */
  26.  
  27. /* C K V F I O  --  Kermit file system support for VAX/VMS.  */
  28.  
  29. /*
  30.   Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  31.   Columbia University Academic Information Systems, New York City.
  32.  
  33.   Copyright (C) 1985, 1994, Trustees of Columbia University in the City of New
  34.   York.  The C-Kermit software may not be, in whole or in part, licensed or
  35.   sold for profit as a software product itself, nor may it be included in or
  36.   distributed with commercial products or otherwise distributed by commercial
  37.   concerns to their clients or customers without written permission of the
  38.   Office of Kermit Development and Distribution, Columbia University.  This
  39.   copyright notice must not be removed, altered, or obscured.
  40. */
  41.  
  42. /*
  43.   Originally adapted to VMS by:
  44.   Stew Rubenstein, Harvard University Chemical Labs, 1985,
  45.   Contributors:
  46.   Frank da Cruz (fdc), Columbia University Center, New York, NY (1985-present)
  47.   Stew Rubenstein, Harvard University Chemical Labs, Cambridge, MA (1985)
  48.   Martin Minow (MM), Digital Equipment Corporation, Maynard MA (1985)
  49.   Dan Schullman (DS), Digital Equipment Corporation, Maynard MA (1985)
  50.   Mark Buda (MAB), Digital Equipment Corporation, Nashua, NH (1989-90)
  51.   Terry Kennedy (TMK), St. Peter's College, Jersey City, NJ (1990-present)
  52.   William Bader (WB), Lehigh University, Bethlehem, PA (1990-93)
  53.   Gary Mussar (GM), Bell-Northern Research, Ottawa, Canada (1991)
  54.   James Sturdevant (JS) (1992)
  55.   Tarjei T. Jensen (ttj), Norwegian Hydrographic Service (1993-94)
  56.   Mark Berryman (mb), SAIC (1994)
  57. */
  58. /* Edit history
  59.  * 003 20-Mar-85 MM  fixed fprintf bug in zsout.c
  60.  * 004 21-Mar-84 MM  create text files in variable-stream.
  61.  * 005  8-May-85 MM  filled in zkself (not tested), fixed other minor bugs
  62.  * 006  5-Jul-85 DS  handle version number in zltor, zrtol
  63.  * 007 11-Jul-85 fdc fix zclose() to give return codes
  64.  * 008 19-Mar-86 fdc Fix system() for "!", zopeni() for REMOTE commands.
  65.  * 008 17-Sep-87 fdc Define PWDCMD.
  66.  * 009 (???)
  67.  * 010 24-Jan-88 fdc Add zgtdir() function, even tho it doesn't work...
  68.  * 011 14-Feb-89 mab Make zgtdir() work in V2/V3 C envirements,
  69.  *             Make zkself work using delprc() using Will Wood's changes.
  70.  * 012 26-Feb-89 mab Add function that searches for kermit.ini file in various
  71.  *                   ways
  72.  * 013 05-Mar-89 mab Add Barry Archers enhancements/fixes.
  73.  * 014 15-Mar-89 mab Check for non-null data, not array of pointers in
  74.  *                   zkermini
  75.  * 015 04-Apr-89 mab Add latent support for attribute packet.  Clean up
  76.  *             file name translation code.
  77.  * 016 05-Apr-89 mab Add PWP code to optimize packetizing.
  78.  * 017 16-Apr-89 mab PWP changes broke REMOTE command.  Fixed.
  79.  * 018 18-Apr-89 mab #ifdef chkfn.  This removes a lot of overhead.
  80.  *             Add code to gtdir() for V4.x.
  81.  * 019 12-Jun-89 mab Add PWP's encode logic
  82.  * 020 09-Jul-89 mab Add logic to check for system() availability
  83.  * 021 10-Jul-89 mab Fix SHOW USER USERNAME.  Added space after 'SHOW USER'.
  84.  * 022 27-Sep-89 mab Added zmail/zprint, plus added changes from CKUFIO.C
  85.  * 023 01-Dec-89 mab Add RMS file support
  86.  * 024 20-Jul-90 wb  Add support for old VAX C & VMS versions + zstrip & rename
  87.  * 025 29-Jul-90 tmk Change space command to show avail, not used (match spec)
  88.  * 026 29-Jul-90 tmk Hack out the RMS stuff - it can come back when it works
  89.  * 027 29-Jul-90 tmk Likewise the VMS V3 stuff - ancient history
  90.  * 028 29-Jul-90 tmk Replace the attribute stuff. It now works.
  91.  * 029 31-Jul-90 tmk Fix CWD command (via hack)
  92.  * 030 31-Jul-90 tmk Fix assorted bugs preventing remote commands from working
  93.  * 031 31-Jul-90 tmk Correctly handle interrupted remote commands
  94.  * 032 04-Aug-90 tmk Start work on full RMS support for input files
  95.  * 033 04-Aug-90 tmk Tack LF on end of subprocess output lines
  96.  * 034 04-Aug-90 tmk Complete work on full RMS support for input files
  97.  * 035 04-Aug-90 tmk Add support for Fortran CC, fill in recfm data
  98.  * 036 05-Aug-90 tmk Add trailing CRLF on print format files
  99.  * 037 12-Aug-90 tmk Start work on full RMS support for output files
  100.  * 038 12-Aug-90 tmk Honor first free byte (FFB) on SENDs
  101.  * 039 13-Aug-90 tmk Finished first cut of full RMS support for output files
  102.  * 040 29-Sep-90 tmk Add iswild() from FDC for edit 157
  103.  * 041 06-Oct-90 tmk Add filetype IMAGE support for outbound transfers. Note
  104.  *             that this doesn't currently work as the receiver overrides
  105.  *             it (must talk to fdc).
  106.  * 042 06-Oct-90 tmk Make logfiles MRS=80. Being able to edit them outweighs
  107.  *             any use for un-split lines.
  108.  * 043 17-Oct-90 wb  Make zclosf() remove delete mailboxes & deassign channels
  109.  *                   used to talk to the subprocess, so quotas are not used
  110.  *                   up after repeated mailbox use (installed by fdc).
  111.  * 044 19-Oct-90 fdc Changed zxcmd() to use the fp[] arrays in the normal way,
  112.  *                   and zsyscmd to call zxcmd(ZIFILE) rather than
  113.  *                   zxcmd(ZSYSFN).  Got rid of all calls to system(), used
  114.  *                   zsyscmd() instead, so commands like DIR could be
  115.  *                   interrupted.  Made zoutdump() return(-1) rather than
  116.  *                   exit() when "line too long for buffer", and increased
  117.  *                   line output buffer from 1K to 4K.
  118.  * 045 01-Nov-90 tmk Corrected behavior of error check on $create call so a
  119.  *             file supersede would work properly.
  120.  * 046 01-Nov-90 tmk Clone binary flag to ofile_bmode so we have a consistent
  121.  *             view of this flag during file operations - the binary flag
  122.  *             tends to toggle when we don't want/expect it to.
  123.  * 047 01-Nov-90 tmk Make IMAGE mode work. Note that image mode is only used
  124.  *             when VMS is sending a file, and includes all record
  125.  *             control characters not normally sent. Only useful in
  126.  *             unusual circumstances.
  127.  * 048 01-Nov-90 tmk Remove spurious \n from zsoutl() which caused debug logs
  128.  *             to have spurious <CR>'s when viewed with editors.
  129.  * 049 02-Nov-90 fdc Adapt to dynamic allocation of file i/o buffers.  Changes
  130.  *                   are within #ifdef DYNAMIC..#else..#endif brackets.
  131.  * 050 02-Nov-90 fdc Make zsyscmd() close inferior process.
  132.  * 051 ??-???-?? ??? Add ckermit_init logical, return 0 on wildcard operations.
  133.  * 052 24-Dec-90 tmk Fix performance problems after 32Kb w/ ASCII receives, fix
  134.              2-nulls-per-32Kb in binary mode bug (actually in ckcker.h,
  135.              this is a placeholder).
  136.  * 053 13-Jan-91 tmk Add support for SET FILE RECORD-LENGTH.
  137.  * 054 14-Jan-91 tmk Fix cases of /x/CR/LF/y/ and /x/CR/LF/y/CR/LF/ in ASCII
  138.  *             file receives.
  139.  * 055 16-Jan-91 tmk Log requested file type to debug log when receiving.
  140.  * 056 16-Jan-91 tmk Add support for all zstime() functions.
  141.  * 057 17-Jan-91 tmk Add support for zchkspa() function.
  142.  * 058 17-Jan-91 tmk Move debug() call into if clause in zxpand, per fdc.
  143.  * 059 18-Jan-91 tmk Support remote (DECnet) file accesses.
  144.  * 060 18-Jan-91 tmk Fix READ command.
  145.  * 061 30-Jan-91 tmk Support creation of UNDEFINED file types for brain-dead
  146.  *             BASIC implementation.
  147.  * 062 30-Jan-91 tmk Fix REMOTE commands when VERIFY is set.
  148.  * 063 29-Mar-91 tmk Add padding factor for received text files to accomodate
  149.  *             space taken up by record delimiters (per fdc).
  150.  * 064 29-Mar-91 gm  Remove unnecessary mem-mem moves during ASCII receives.
  151.  *             (Installed by tmk. To back out, #define OLD_WAY).
  152.  * 065 30-Mar-91 tmk First pass at implementing LABELED. Send only, dummy
  153.  *             data records.
  154.  * 066 02-Apr-91 tmk Finish first pass at LABELED. Send VMS filename, attri-
  155.  *             butes. Still need ACL's, "hidden" char. longword, recep-
  156.  *             tion.
  157.  * 067 09-Apr-91 tmk LABELED bugfixes - VMSFILE is 70 bytes, not 74, use the
  158.  *             xab$w_lrl field instead of rab$w_rsz, fab$w_deq instead
  159.  *             of xab$w_rsz, fab$b_bks instead of xab$b_bkz, always pro-
  160.  *             cess an even multiple of 512 bytes when LABELED.
  161.  * 068 14-Apr-91 tmk Don't use C definition of fab$b_journal as it doesn't ex-
  162.  *             ist before C V3.1. Compute it ourselves instead.
  163.  * 069 15-Apr-91 tmk Initial work on retrieving ACL information for LABELED.
  164.  * 070 16-Apr-91 tmk Make edits 066-069 compatible with DECnet.
  165.  * 071 21-May-91 tmk Address R. Weiner QAR item 2 (filesize).
  166.  * 072 21-Jun-91 tmk Check (and prohibit) spawns from captive accounts.
  167.  * 073 21-Jun-91 tmk Fix session logging (for Charlie Luce/DECUServe).
  168.  * 074 21-Jun-91 tmk Rework 071 to only apply to SPAWN/PUSH and not to the
  169.  *             pseudo-builtins like DEL, SPACE, WHO, PWD, etc.
  170.  * 075 21-Jun-91 tmk Fix possible endless loop when flushing output file in
  171.  *             zclosf() after zoutdump() error.
  172.  * 076 21-Jun-91 tmk First pass on handling inbound LABELED files.
  173.  * 077 14-Nov-91 tmk Fix zprint(), zmail() (need to use system() for these).
  174.  *                   This is a partial backout of 044.
  175.  * 078 14-Nov-91 tmk Various cleanups.  Delete files after successful mailing
  176.  *             or printing, remove dead code inside #ifdef COMMENT and
  177.  *                   #ifdef OLD_WAY, fix typo in spawning message, make sure
  178.  *                   all source lines < 80 chars.
  179.  * 079 22-Nov-91 fdc Change zmail(), zprint() error return values to improve
  180.  *             error reporting.
  181.  * 080 18-Jan-92 tmk Fix REMOTE so output from a remote command correctly dis-
  182.  *             plays on terminal. This has been broken since 040 or so.
  183.  * 081 10-Jun-92 tmk Add William Bader's fix for fixed-length files which have
  184.  *                   record attributes.
  185.  * 082 03-Jul-92 tmk Fix really bad bug introduced in 081 (which made *all*
  186.  *                   fixed-format files be sent as text).
  187.  * 083 15-Jul-92 jah Fix fencepost error in zoutdump when line breaks at 32K.
  188.  * 084 03-Aug-92 fdc Remove current directory from init file search.
  189.  * 085 26-Aug-92 tmk Add Bernd Onasch's fix for fgen().
  190.  * 086 28-Aug-92 tmk Fix bug reported by Bill Hoelzer where C-K would execute
  191.  *             a file named "." as a C-K initialization file.
  192.  * 087 04-Sep-92 tmk Fix bug reported by Chuck McMichael where C-K would not
  193.  *             set the FFB properly when receiving a labeled file which
  194.  *             did not have the FFB on a record boundary.
  195.  * 088 09-Sep-92 tmk Fix Hunter Goatley's problem with SPAWN command ignoring
  196.  *             Ctrl-C.
  197.  * 089 11-Sep-92  js Fixed malloc() in zmail().
  198.  * 090 28-Oct-92 tmk Fix null-byte error introduced by 087. Gee, this looked
  199.  *             so simple when I designed it.
  200.  * 091 02-Nov-92 tmk Start work on fixing spawn/push/remote commands, due to
  201.  *             popular whining.
  202.  * 092 03-Nov-92 tmk Finish up initial 091 work. Vote for Kermit!
  203.  * 093 03-Nov-92 fdc Change zkermini() to work with "-y" command-line option.
  204.  * 094 04-Nov-92 tmk Make zxpand() not return all files if given null string.
  205.  * 095 05-Nov-92 fdc Make zxcmd(), zclose(), etc, handle ZRFILE (OPEN !READ).
  206.  * 096 17-Feb-93 fdc prevent zopeno from calling zstime if date struct is NULL,
  207.  *                   and add support for ZMFILE (misc output file).
  208.  * 097 08-Apr-93 tmk Correctly handle "international VMS" which uses <> instead
  209.  *             of [] for directory delimiters.
  210.  * 098 16-May-93 fdc ANSIfication for GNU CC, from James Sturdevant, plus
  211.  *                   add FAB$M_PRN to list of text-file types, for VMS batch
  212.  *                   logs.
  213.  * 099 07-Jun-93 fdc Fix calculation of file size in zchki(), fix declaration
  214.  *                   of mbxnam[] (add one to size) to prevent overflow, which
  215.  *                   would result in failure of server to respond to REMOTE
  216.  *                   directory, etc.  Both fixes from Bill Glass.
  217.  * 100 21-Jun-93 fdc file_date[] and attr_date[] declarations in zstime()
  218.  *                   changed from long to unsigned long to prevent signed date
  219.  *                   comparisons, which could prevent SET FILE COLLISION
  220.  *                   UPDATE from working.  From James Sturdevant.
  221.  * 101  8-Aug-93 fdc Add types to all function declarations.
  222.  * 102 18-Aug-93 ttj Minor updates in zsattr() and do_label_recv() mainly to
  223.  *                   quieten the compiler (which had every reason to complain).
  224.  * 103  5-Nov-93 wb  Add isdir() function.
  225.  * 104  8-Nov-93 wb  Add zfcdate() function.
  226.  * 105 25-Nov-93 fdc Correct record format for session log; change name of
  227.  *                   password structure member of zattr struct.
  228.  * 106 22-Dec-93 tmk Correct fd "leakage" in OPEN READ/CLOSE READ pairs.
  229.  * 107 26-Feb-94 mb  Addition of zmkdir() routine.
  230.  * 108 27-Mar-94 tmk Increase max record size for logs from 80 to 254.
  231.  *                   Add support for file append operations.
  232.  *                   Make zkself() retry a few times to avoid zombies on BYE.
  233.  * 109  5-Apr-94 tmk Fix xx->lengthk not being set in zsattr().
  234.  * 110  8-Jun-94 tmk Use private fab in zchki (fixes OPEN READ bug).
  235.  * 111 17-Jun-94 tmk Let zsattr() work even if there is no rights database.
  236.  * 112  7-Jul-94 js  A couple small ANSIfications for gcc.
  237.  * 113  7-Aug-94 fdc Make zshcmd()/zsyscmd() return proper return code, with
  238.  *                   help from Larry Henry at TWG.  Still not quite right...
  239.  * 114 29-Sep-94 fdc Increase max wildcard matches from 500 to 4096.
  240.  * 115  4-Oct-94 mb  Add support for RESEND.
  241.  * 116 26-Oct-94 mb  Minor fix to 115.
  242.  * 117  1-Nov-94 wb  A couple #ifdefs added to allow compilation in VMS v4.
  243.  */
  244.  
  245. /* Definitions of some VMS system commands */
  246.  
  247. char *DIRCMD = "directory ";        /* For directory listing */
  248. char *DIRCM2 = "directory ";        /* For directory listing, no args */
  249. char *DELCMD = "delete ";        /* For file deletion */
  250. char *TYPCMD = "type ";            /* For typing a file */
  251. char *SPACMD = "show quota ";         /* Space/quota of current directory */
  252. char *SPACM2 = "show quota ";         /* Space/quota of specified dir */
  253. char *WHOCMD = "show users ";        /* For seeing who's logged in */
  254. char *PWDCMD = "show default ";        /* For seeing current directory */
  255.  
  256. /*
  257.   Functions (n is one of the predefined file numbers from ckermi.h):
  258.  
  259.    zopeni(n,name)   -- Opens an existing file for input.
  260.    zopeno(n,name)   -- Opens a new file for output.
  261.    zclose(n)        -- Closes a file.
  262.    zchin(n)         -- Gets the next character from an input file.
  263.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  264.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  265.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  266.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  267.    zchki(name)      -- Check if named file exists and is readable, return size.
  268.    zchko(name)      -- Check if named file can be created.
  269.    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
  270.    znewn(name,s)    -- Make a new unique file name based on the given name.
  271.    zdelet(name)     -- Delete the named file.
  272.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  273.    znext(string)    -- Returns the next file from the list in "string".
  274.    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
  275.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  276.    zrtol(n1,n2)     -- Convert remote filename into local form.
  277.    zltor(n1,n2)     -- Convert local filename into remote form.
  278.    zchdir(dirnam)   -- Change working directory.
  279.    zhome()          -- Return pointer to home directory name string.
  280.    zkself()         -- Log self out
  281.    zsattr(struc zattr *) -- Return attributes for file which is being sent.
  282.    zkermini(n1,n2)  -- Find kermit.ini using default scanning process
  283.  */
  284.  
  285. /* Includes */
  286.  
  287. #define CKVFIO_C
  288. #include "ckcdeb.h"
  289. #include "ckcasc.h"
  290. #include "ckcker.h"
  291. #include "ckvvms.h"
  292. #include <stdio.h>
  293. #include <types.h>
  294. #include <stat.h>
  295. #include <ctype.h>
  296. #include <time.h>
  297. #include <errno.h>
  298. #include <rms.h>
  299. #include <ssdef.h>
  300. #include <descrip.h>
  301. #include <dvidef.h>
  302. #include <dcdef.h>
  303. #include <iodef.h>
  304. #include <jpidef.h>
  305. #include <signal.h>
  306. #include <string.h>
  307. #include <syidef.h>
  308. #include <uaidef.h>
  309.  
  310. /* rms.h above includes nam, fab, xab and rmsdef */
  311. /* vms v4 headers do not check against multiple inclusion */
  312. /* ifndefs below prevent multiple declaration of FAB and NAM structs */
  313.  
  314. #ifndef FAB$C_BID
  315. #include <fab.h>            /* These are needed for isdir() */
  316. #endif /* FAB$C_BID */
  317.  
  318. #ifndef NAM$C_BID
  319. #include <nam.h>
  320. #endif /* NAM$C_BID */
  321.  
  322. #include <lnmdef.h>
  323. #include <rmsdef.h>
  324.  
  325. #define MAXWLD 4096            /* Maximum wildcard filenames */
  326.  
  327. /* external def. of things used in buffered file input and output */
  328.  
  329. #ifdef DYNAMIC
  330. extern CHAR *zinbuffer, *zoutbuffer;
  331. #else
  332. extern CHAR zinbuffer[], zoutbuffer[];
  333. #endif /* DYNAMIC */
  334.  
  335. extern CHAR *zinptr, *zoutptr;
  336. extern int zincnt, zoutcnt;
  337. extern int binary;
  338. extern int frecl;
  339. extern int rcflag;
  340.  
  341. extern long vernum;
  342.  
  343. /* Declarations */
  344.  
  345. FILE *fp[ZNFILS] = {             /* File pointers */
  346.     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
  347. };
  348.  
  349. static long iflen = -1;            /* Input file length */
  350. static long oflen = -1;            /* Output file length */
  351. static int fcount;            /* Number of files in wild group */
  352. static char nambuf[255];        /* maximum size of a file spec */
  353. static char cwdbuf[NAM$C_MAXRSS];
  354. static struct iosb_struct tmpiosb;    /* For QIOW */
  355.  
  356. extern unsigned long vms_status;    /* Used by CHECK_ERR */
  357.  
  358. static int cflag;            /* Flag indicating console in use */
  359.  
  360. int check_spawn(void);
  361. int do_label_recv(void);
  362. int do_label_send(char *name);
  363. static int parse_fname(char *cp, int cp_len, char *defnam, int flag);
  364. int fgen(char *pat, char *resarry[], int len);
  365. char *getenv(), *strcpy();
  366.  
  367. /* static */                /* Not static any more! */
  368. char *mtchs[MAXWLD],            /* Matches found for filename */
  369.   **mtchptr;                /* Pointer to current match */
  370.  
  371. static unsigned short mbx_chan;        /* Mailbox chan for REMOTE commands */
  372. static int subprocess_input = 0, sub_count;
  373. static char *sub_ptr, sub_buf[SUB_BUF_SIZE];
  374.  
  375. #define    SUPERSAFE            /* For safe subprocesses */
  376. static unsigned long sub_pid;
  377.  
  378. /*
  379.  * Structures for input (SEND) file
  380.  */
  381.  
  382. static    struct FAB fab_ifile;
  383. static    struct RAB rab_ifile;
  384. static    struct XABDAT xabdat_ifile;
  385. static    struct XABFHC xabfhc_ifile;
  386. static    struct XABPRO xabpro_ifile;
  387. static    struct XABALL xaball_ifile;
  388. static    int ifile_bmode;
  389. static    int ifile_bcount;
  390. static    char aclbuf[512];
  391. static    unsigned long uchar = 0;
  392.  
  393. /*
  394.  * Structures for output (RECEIVE) file
  395.  */
  396.  
  397. static    struct FAB fab_ofile;
  398. static    struct RAB rab_ofile;
  399. static    struct XABDAT xabdat_ofile;
  400. static    struct XABFHC xabfhc_ofile;
  401. static    struct XABPRO xabpro_ofile;
  402. static    struct XABALL xaball_ofile;
  403. static    struct XABRDT xabrdt_ofile;
  404. static    int ofile_dump;
  405. static    int ofile_bmode;
  406. static    int ofile_lblopts;
  407. static    int ofile_lblproc;
  408. static    char revdat[8];
  409. static    unsigned short revnum;
  410. static    char ofile_vmsname[255];
  411. static    char ofile_vmsacl[512];
  412. static    int ofile_acllen;
  413. static    short ofile_ffb;
  414.  
  415. /*
  416.  * Common RMS items
  417.  */
  418. static    unsigned long int rms_sts;
  419.  
  420. /*  I S W I L D  --  Tells whether filespec "str" is wild  */
  421. /*  Returns 0 if not wild, 1 if wild */
  422.  
  423. int
  424. iswild(str) char *str; {
  425.     char c;
  426.     while ((c = *str++) != '\0')
  427.       if (c == '*' || c == '%') return(1);
  428.     return(0);
  429. }
  430.  
  431. #ifdef CK_TMPDIR
  432.  
  433. /*  I S D I R  --  Tells if string pointer s is the name of a directory. */
  434. /*  Returns 1 if directory, 0 if not a directory. */
  435. /*  Contributed by William Bader. */
  436.  
  437. int
  438. isdir(s) char *s; {
  439.     int x;
  440.     struct stat statbuf;
  441.     int i, s_len;
  442.     char *full_name;
  443.     static char dot_dir[] = ".dir";
  444.     static char zero_dir[] = "[000000]";
  445.  
  446.     char name_buf[255];            /* Was 512 but that's too big */
  447.                     /* for unsigned char! */
  448.     struct FAB fab;
  449.     struct NAM nam;
  450.  
  451.     struct dsc$descriptor_s indesc;
  452.     $DESCRIPTOR(lnmtable, "LNM$FILE_DEV");
  453.     int new_len;
  454.     int serial_num;
  455.     struct {
  456.     short length;
  457.     short code;
  458.     char *address;
  459.     int *len;
  460.     int term;
  461.     } itemlist;
  462.  
  463.     if (!s) return(0);
  464.     if (!*s) return(0);
  465.  
  466.     /* Determine if this is something we can SET DEFAULT to...  */
  467.  
  468.     s_len = strlen(s);
  469.  
  470.     if (s[s_len-1] != ':' && s[s_len-1] != ']' && s[s_len-1] != '>') {
  471.         /* Does not look like a directory name */
  472.         return(0);
  473.     }
  474.  
  475.     /* Check that the directory part is valid... */
  476.  
  477.     if (s[s_len-1] == ']' || s[s_len-1] == '>') {
  478.         fab = cc$rms_fab;
  479.         nam = cc$rms_nam;
  480.  
  481.         fab.fab$l_dna = 0;
  482.         fab.fab$b_dns = 0;
  483.  
  484.         fab.fab$l_fna = s;
  485.         fab.fab$b_fns = s_len;
  486.  
  487.         fab.fab$l_fop = 0;
  488.         fab.fab$w_ifi = 0;
  489.         fab.fab$l_nam = &nam;
  490.  
  491.         nam.nam$l_esa = name_buf;
  492.         nam.nam$b_ess = sizeof(name_buf);
  493.         nam.nam$b_nop = 0;
  494.         nam.nam$l_rlf = 0;
  495.         nam.nam$l_rsa = 0;
  496.         nam.nam$b_rsl = 0;
  497.         nam.nam$l_fnb = 0;
  498.  
  499.         i = sys$parse(&fab, 0, 0);
  500. #ifdef COMMENT
  501.     printf("parse returned %d 0x%x, nam fnb is %d 0x%x\n",
  502.            i, i, nam.nam$l_fnb, nam.nam$l_fnb);
  503. #endif /* COMMENT */
  504.         if ((i & 1) == 0) return(0);
  505.     }
  506.  
  507.     /* Check that the logical name is valid */
  508.  
  509.     i = s_len - 1;
  510.     while (i >= 0 && s[i] != ':') i--;
  511.  
  512.     if (i >= 0 && s[i] == ':') {
  513.         if (i == 0) return(0);        /* Single colon (:) */
  514.         if (s[i-1] == ':') {
  515.             if (i > 1) return(1);    /* DECnet node name (blah::) */
  516.             else return(0);        /* or :: alone. */
  517.         }
  518.     s_len = i;
  519.     full_name = malloc(s_len + 1);
  520.     if (!full_name) return(0);
  521.     /* Logicals must be upper case */
  522.     for (i = 0; i < s_len; i++) {
  523.         full_name[i] = s[i];
  524.         if (full_name[i] >= 'a' && full_name[i] <= 'z')
  525.         full_name[i] -= ('a' - 'A');
  526.     }
  527.     
  528.     indesc.dsc$w_length = s_len;
  529.     indesc.dsc$a_pointer = full_name;
  530.     indesc.dsc$b_class = DSC$K_CLASS_S;
  531.     indesc.dsc$b_dtype = DSC$K_DTYPE_T;
  532.  
  533.     itemlist.length = new_len = sizeof(name_buf);
  534.     itemlist.code = LNM$_STRING;
  535.     itemlist.address = name_buf;
  536.     itemlist.len = &new_len;
  537.     itemlist.term = 0;
  538.  
  539.     i = sys$trnlnm(0, &lnmtable, &indesc, 0, &itemlist);
  540.     if (i != SS$_NORMAL || new_len < 0) new_len = 0;
  541.     if (new_len >= sizeof(name_buf)) new_len = sizeof(name_buf) - 1;
  542.     name_buf[new_len] = '\0';
  543.  
  544. #ifdef COMMENT
  545.     printf("trnlnm result %d 0x%x, '%.*s'\n", i, i, new_len, name_buf);
  546. #endif /* COMMENT */
  547.  
  548.     free(full_name);
  549.  
  550.     if (new_len == 0) {
  551.         /* Could still be a device name. */
  552.         /* Only disks have serial numbers... */
  553.         serial_num = 0;
  554.         itemlist.length = new_len = sizeof(serial_num);
  555.         itemlist.code = DVI$_SERIALNUM;
  556.         itemlist.address = (char *) &serial_num;
  557.         itemlist.len = &new_len;
  558.         itemlist.term = 0;
  559.         i = sys$getdvi(0, 0, &indesc, &itemlist, 0, 0, 0, 0);
  560. #ifdef COMMENT
  561.         printf("getdvi ret %d 0x%x, serial %d len %d\n",
  562.            i, i, serial_num, new_len);
  563. #endif /* COMMENT */
  564.         return(((i & 1) == 1 && new_len > 0) ? 1: 0);
  565.  
  566.     } else if (name_buf[new_len-1] == ':' ||
  567.             name_buf[new_len-1] == ']' ||
  568.             name_buf[new_len-1] == '>') {
  569.         /* Check returned value */
  570.         if (new_len > 2 &&
  571.             (name_buf[new_len-1] == ']' || name_buf[new_len-1] == '>') &&
  572.             name_buf[new_len-2] == '.') {
  573.             /* Remove trailing dot in directory of logical name */
  574.             name_buf[new_len-2] = name_buf[new_len-1];
  575.             name_buf[new_len-1] = '\0';
  576.         }
  577.         return( isdir(name_buf) );
  578.     } else {
  579.         /* Logical name is just a random string signifying nothing */
  580.         return( 0 );
  581.     }
  582.     }
  583.  
  584.     return(1);
  585. }
  586. #endif /* CK_TMPDIR */
  587.  
  588. /*  Z K S E L F  --  Log self out.  */
  589.  
  590. int
  591. zkself() {
  592.     int i;
  593.     unsigned long int rms_s;
  594. /*
  595.   We need a better way.  If C-Kermit was spawned, this does not log out the
  596.   whole job.    It also does not hang up LAT terminal sessions.
  597. */
  598.     for (i = 0; i < 10; i++) {
  599.     rms_s = sys$delprc(0,0);    /* Maybe some output is still */
  600.     debug(F101,"zkself rms_s","",rms_s);
  601.     if (rms_s == SS$_NORMAL)    /* queued; try a few times... */
  602.       exit(1);
  603.     sleep(1);
  604.     }
  605.     exit(rms_s == SS$_NORMAL);
  606.     return(0); /* dummy - required as this is called in a non-void context */
  607. }
  608.  
  609. /*  Z O P E N I  --  Open an existing file for input. */
  610.  
  611. int
  612. zopeni(n,name) int n; char *name; {
  613.     debug(F111," zopeni",name,n);
  614.     debug(F101,"  fp","",(int) fp[n]);
  615.     if (chkfn(n)) return(0);
  616.     if (n == ZSYSFN) {            /* Input from a system function? */
  617.     debug(F110," zopeni called with ZSYSFN, failing!",name,0);
  618.     *nambuf = '\0';            /* No filename. */
  619.     return(0);            /* fail. */
  620.     }
  621.     zincnt = 0;                /* Initializing these couldn't hurt */
  622.     zinptr = zinbuffer;
  623.     if (n == ZSTDIO) {            /* Standard input? */
  624.     if (isatty(0)) {
  625.         ermsg("?Terminal input not allowed\n");
  626.         debug(F110," zopeni attempted input from unredirected stdin","",0);
  627.         return(0);
  628.     }
  629.     fp[ZIFILE] = stdin;
  630.     return(1);
  631.     }
  632. /*
  633.  * We open the file but waffle on the access mode we're going to use. We then
  634.  * inspect the file characteristics to see if the organization is fixed or un-
  635.  * defined. If it is, we convert to block mode operation. This is needed since
  636.  * VMS maintains a "first free byte" field to tell us how much of the last rec-
  637.  * ord really contains data, but won't terminate reads at that point. Thus, if
  638.  * we want to SEND the exact same file we RECEIVEd, we have to honor the FFB
  639.  * internally.
  640.  */
  641.     if (n == ZIFILE || n == ZRFILE) {
  642.     ifile_bmode = 0;
  643.     ifile_bcount = 0;
  644.     fab_ifile = cc$rms_fab;
  645.     fab_ifile.fab$b_fac = FAB$M_BRO | FAB$M_GET;
  646. /*
  647.  * Some non-VMS DECnet implementations don't allow switching modes, so set
  648.  * block-I/O only mode if the user said SET FILE TYPE IMAGE or LABELED.
  649.  */
  650.     if (binary == XYFT_I || binary == XYFT_L)
  651.         fab_ifile.fab$b_fac = FAB$M_BIO | FAB$M_GET;
  652.     fab_ifile.fab$l_fna = name;
  653.     fab_ifile.fab$b_fns = strlen(name);
  654.     fab_ifile.fab$l_xab = (char *)&xabdat_ifile;
  655.     rab_ifile = cc$rms_rab;
  656.     rab_ifile.rab$l_fab = &fab_ifile;
  657.     xabdat_ifile = cc$rms_xabdat;
  658.     xabdat_ifile.xab$l_nxt = (char *)&xabfhc_ifile;
  659.     xabfhc_ifile = cc$rms_xabfhc;
  660.     xabfhc_ifile.xab$l_nxt = (char *)&xabpro_ifile;
  661.     xabpro_ifile = cc$rms_xabpro;
  662.     memset(&aclbuf, 0, sizeof(aclbuf));
  663.     xabpro_ifile.xab$l_aclsts = SS$_NORMAL;        /* Oh Joy! DECnet */
  664.     xabpro_ifile.xab$l_aclbuf = (char *)&aclbuf;
  665.     xabpro_ifile.xab$w_aclsiz = sizeof(aclbuf);
  666.     xabpro_ifile.xab$l_aclctx = 0;
  667.     xabpro_ifile.xab$l_nxt = (char *)&xaball_ifile;
  668.     xaball_ifile = cc$rms_xaball;
  669.  
  670.     rms_sts = sys$open(&fab_ifile);
  671.     if (rms_sts != RMS$_NORMAL) {
  672.         debug(F101," zopeni $open failed, status","",rms_sts);
  673.         debug(F101," zopeni $open failed, stv","",fab_ifile.fab$l_stv);
  674.         return(0);
  675.     }
  676.     if (!(xabpro_ifile.xab$l_aclsts & 1)) {
  677.         if (xabpro_ifile.xab$l_aclsts != SS$_ACLEMPTY) {
  678.         debug(F101," zopeni $open ACL failed, status","",
  679.           xabpro_ifile.xab$l_aclsts);
  680.         return(0);
  681.         }
  682.     }
  683. /*
  684.  * We have the file opened. See if it's fixed or undefined format...
  685.  */
  686.     if (fab_ifile.fab$b_rfm == FAB$C_UDF) {
  687.         debug(F100," zopeni undefined file format - using blk I/O","",0);
  688.         ifile_bmode = 1;
  689.     }
  690.     if (fab_ifile.fab$b_rfm == FAB$C_FIX) {
  691.         if ((fab_ifile.fab$b_rat & (FAB$M_FTN | FAB$M_CR | FAB$M_PRN))
  692.         == 0) {
  693.         debug(F100," zopeni fixed file format - using blk I/O","",0);
  694.         ifile_bmode = 1;
  695.         }
  696.       }
  697.     debug(F101," zopeni binary flag at open","",binary);
  698.     if (binary == XYFT_I) {
  699.         debug(F100," zopeni using IMAGE mode by user request","",0);
  700.         ifile_bmode = 1;
  701.     }
  702.     if (binary == XYFT_L) {
  703.         debug(F100," zopeni using LABELED mode by user request","",0);
  704.         ifile_bmode = 2;
  705.     }
  706.     rab_ifile.rab$l_rop = 0;
  707.     rms_sts = sys$connect(&rab_ifile);
  708.     if (rms_sts != RMS$_NORMAL) {
  709.         debug(F101," zopeni $connect failed, status","",rms_sts);
  710.         return(0);
  711.     }
  712.     debug(F100," zopeni RMS operations completed ok","",0);
  713.     fp[n] = fopen("NLA0:","r");    /* it wants a fp, give it one */
  714.     zincnt = 0;            /* reset input buffer */
  715.     if (binary == XYFT_L)
  716.         do_label_send(name);    /* make a file label */
  717.     return(1);
  718.     }
  719.     zincnt = 0;                /* Reset input buffer */
  720.     fp[n] = fopen(name,"r");        /* Real file. */
  721.     debug(F111," zopeni", name, (int) fp[n]);
  722.     if (fp[n] == NULL) perror("zopeni");
  723.     return((fp[n] != NULL) ? 1 : 0);
  724. }
  725.  
  726. /*  Z O P E N O  --  Open a new file for output.  */
  727.  
  728. int
  729. zopeno(n,name,zz,fcb)
  730.     int n; char *name; struct zattr *zz; struct filinfo *fcb; {
  731.  
  732. /* As of Version 5A, the attribute structure and the file information */
  733. /* structure are included in the arglist. */
  734.  
  735.     int fildes;
  736.     int writeover = 0;
  737.     extern int rs_len;
  738.     char p[4];
  739.  
  740.     if (n != ZDFILE)
  741.       debug(F111," zopeno",name,n);
  742.     if (chkfn(n) != 0) return(0);
  743.  
  744.     zoutcnt = 0;        /* (PWP) reset output buffer */
  745.     zoutptr = zoutbuffer;
  746.     cflag = 0;            /* default to not using console */
  747.  
  748. /*
  749.  * Open Terminal or STDIO
  750.  */
  751.  
  752.     if ((n == ZCTERM) || (n == ZSTDIO)) {
  753.     fp[ZOFILE] = stdout;
  754.     cflag = 1;        /* say using console */
  755.     if (n != ZDFILE)
  756.         debug(F101," fp[]=stdout", "", (int) fp[n]);
  757.     return(1);
  758.     }
  759.  
  760. /*
  761.  * Open Debug, Transaction, Packet, Session logfile, or a Write file
  762.  * The only other possibility at this point is the output file, so we test that
  763.  */
  764.  
  765.     if (fcb)
  766.       debug(F101," zopeno fcb disposition", "", fcb->dsp);
  767.     if (zz)
  768.       debug(F111," zopeno zz disposition",zz->disp.val,zz->disp.len);
  769.  
  770.     if (n != ZOFILE) {
  771.     strcpy(p,"w");                /* Assume write/create mode */
  772.     if (fcb) {                /* If called with an FCB... */
  773.         if (fcb->dsp == XYFZ_A)        /* Does it say Append? */
  774.         strcpy(p,"a");            /* Yes. */
  775.     }
  776.     if (n != ZSFILE)
  777.       /* was mrs = 80; 254 is max record size for EDT */
  778.       fp[n] = fopen(name, p, "rat=cr", "rfm=var", "mrs=254");
  779.     else
  780.       fp[n] = fopen(name, p, "ctx=stm", "rfm=stm");
  781.     if (fp[n] == NULL) {        /* Failed */
  782.         debug(F101,"zopeno VAXCRTL failed errno","",errno);
  783.         perror(name);        /* Print system error message */
  784.     } else {            /* Didn't fail */
  785.         debug(F100,"zopeno VAXCRTL ok", "", 0);
  786.     }
  787.     return((fp[n] != NULL) ? 1 : 0);
  788.     }
  789.  
  790. /*
  791.  * Open a file to store data being RECEIVEd
  792.  */
  793.  
  794.     if (n == ZOFILE) {
  795.     switch (binary) {
  796.         case XYFT_T:
  797.         debug(F100," zopeno receiving TEXT file","",0);
  798.         break;
  799.         case XYFT_B:
  800.         debug(F100," zopeno receiving BINARY file","",0);
  801.         break;
  802.         case XYFT_I:
  803.         debug(F100," zopeno receiving IMAGE file-program bug!","",0);
  804.         break;
  805.         case XYFT_L:
  806.         debug(F100," zopeno receiving LABELED file","",0);
  807.         break;
  808.         case XYFT_U:
  809.         debug(F100," zopeno receiving UNDEFINED file","",0);
  810.         break;
  811.         default:
  812.         debug(F101," zopeno unknown file type","",binary);
  813.     }
  814.     ofile_bmode = binary;
  815.     ofile_dump = 0;
  816.     ofile_ffb = -1;
  817.     fab_ofile = cc$rms_fab;
  818.     fab_ofile.fab$l_fna = name;
  819.     fab_ofile.fab$b_fns = strlen(name);
  820.     fab_ofile.fab$l_fop = FAB$M_MXV;
  821.     fab_ofile.fab$b_fac = FAB$M_PUT;
  822. /*
  823.  * Note that we could actually implement a true overwrite (step on existing
  824.  * file version) operation here by testing for a new XYFZ_* type as well in
  825.  * the line below, and then simply not doing a RAB$M_EOF about 25 lines down
  826.  * from here. We might also have to do something to reset the file allocation
  827.  * so we aren't left with a large number of "leftover" blocks if the new file
  828.  * is smaller than the old one.
  829.  */
  830.     if (fcb) {
  831.         if (fcb->dsp == XYFZ_A) {
  832.         fab_ofile.fab$l_fop = FAB$M_CIF;
  833. /*
  834.   This is for RESEND.  If the output mode is APPEND and the incoming
  835.   attributes structure (zz) says "Resend", then we know this file is
  836.   being resent.
  837. */
  838.         if (*(zz->disp.val) == 'R' && ofile_bmode)
  839.           writeover = 1;
  840.         debug(F101," zopeno APPENDing, writeover","",writeover);
  841.         if (writeover)
  842.           debug(F101," zopeno RESEND, rs_len","",rs_len);
  843. /*
  844.   So if writeover != 0, we can take rs_len to be the length of the existing
  845.   file to keep, and to write over the rest, which normally should be the final
  846.   block.
  847. */
  848.         }
  849.     }
  850.     if (ofile_bmode) {
  851.         fab_ofile.fab$b_fac = FAB$M_BIO;
  852.         debug(F101," zopeno using record size","",frecl);
  853.         fab_ofile.fab$w_mrs = frecl;
  854.         if (ofile_bmode == XYFT_U)
  855.           fab_ofile.fab$b_rfm = FAB$C_UDF;
  856.         else
  857.           fab_ofile.fab$b_rfm = FAB$C_FIX;
  858.     } else {
  859.         fab_ofile.fab$b_rat = FAB$M_CR;
  860.         fab_ofile.fab$b_rfm = FAB$C_VAR;
  861.     }
  862.     fab_ofile.fab$b_shr = FAB$M_NIL;
  863.     fab_ofile.fab$l_xab = (char *)&xabdat_ofile;
  864.     rab_ofile = cc$rms_rab;
  865.     rab_ofile.rab$l_fab = &fab_ofile;
  866.     if (fcb)
  867.         if ((fcb->dsp == XYFZ_A) && (writeover == 0))
  868.         rab_ofile.rab$l_rop = RAB$M_EOF;
  869.     xabdat_ofile = cc$rms_xabdat;
  870.     xabdat_ofile.xab$l_nxt = (char *)&xabfhc_ofile;
  871.     xabfhc_ofile = cc$rms_xabfhc;
  872.     if (zz)
  873.       zstime(name, zz, 2);        /* set creation date */
  874.     if (ofile_bmode == XYFT_L) {    /* defer open if labeled */
  875.         ofile_lblproc = 0;        /* haven't processed labels yet */
  876.         ofile_lblopts = fcb->lblopts;
  877.         debug(F101," zopeno lblopts","",ofile_lblopts);
  878.         debug(F100," zopeno RMS operations deferred","",0);
  879.     } else {
  880.         rms_sts = sys$create(&fab_ofile);
  881.         if (!(rms_sts & 1)) {
  882.         debug(F101," zopeno $create failed, status","",rms_sts);
  883.         return(0);
  884.         }
  885.         rms_sts = sys$connect(&rab_ofile);
  886.         if (rms_sts != RMS$_NORMAL) {
  887.         debug(F101," zopeno $connect failed, status","",rms_sts);
  888.         return(0);
  889.         }
  890.         if (writeover == 1) {        /* if resend ... */
  891.         rab_ofile.rab$l_bkt = (unsigned long) rs_len >> 9;
  892.         rms_sts = sys$space(&rab_ofile);/* space forward to last */
  893.         rab_ofile.rab$l_bkt = 0;    /* complete block */
  894.         if (rms_sts != RMS$_NORMAL) {
  895.             debug(F101," zopeno $space failed, status","",rms_sts);
  896.             return(0);
  897.         }
  898.         }
  899.         debug(F100," zopeno RMS operations completed ok","",0);
  900.     }
  901.     fp[n] = fopen("NLA0:","r");    /* it wants a fp, give it one */
  902.     return(1);
  903.     }
  904. }
  905.  
  906. /*  Z C L O S E  --  Close the given file.  */
  907.  
  908. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  909.  
  910. int
  911. zclose(n) int n; {
  912.     int x=0;
  913.  
  914.     debug(F101," zclose","",n);
  915.     if (chkfn(n) < 1) return(0);
  916.  
  917. /* If this is the subprocess file, close it to flush output */
  918.  
  919.     if ((n == ZIFILE || (n == ZRFILE)) && (subprocess_input != 0)) {
  920.         debug(F100, "zclose calling zclosf", "", 0);
  921.     return (zclosf(n));
  922.     }
  923.  
  924. /* Input file */
  925.  
  926.     if ((n == ZIFILE) || (n == ZRFILE)) {
  927.     rms_sts = sys$close(&fab_ifile);
  928.     if (rms_sts != RMS$_NORMAL) {
  929.         debug(F101," zclose $close failed, status","",rms_sts);
  930.         return(-1);
  931.     }
  932.     debug(F100," zclose RMS operations completed ok","",0);
  933.     x = fclose(fp[n]);        /* close the dummy file */
  934.     fp[n] = NULL;            /* and mark it so */
  935.     iflen = -1;
  936.     return(1);
  937.     }
  938.  
  939. /* Output file */
  940. /* This can probably be combined with the ZIFILE stuff later */
  941.  
  942.     if (n == ZOFILE) {
  943.     ofile_dump = 1;                /* force complete dump */
  944.     while (zoutcnt != 0) {
  945.         rms_sts = zoutdump();        /* flush buffers to disk */
  946.         if (rms_sts != 0)
  947.         return(-1);            /* in case of error */
  948.     }
  949.  
  950.     if (ofile_bmode == XYFT_L) { /* update revisions if labeled */
  951.         debug(F100," zclose updated labeled revision count","",0);
  952.         memmove(&xabrdt_ofile.xab$q_rdt, revdat, 8);
  953.         memmove(&xabrdt_ofile.xab$w_rvn, &revnum, 2);
  954.     }
  955.     if (cflag != 1) {
  956.         rms_sts = sys$close(&fab_ofile);
  957.         if (rms_sts != RMS$_NORMAL) {
  958.         debug(F101," zclose $close failed, status","",rms_sts);
  959.         return(-1);
  960.         }
  961.         x = fclose(fp[n]);            /* close the dummy file */
  962.     } else {
  963.         cflag = 0;
  964.     }
  965.     debug(F100," zclose RMS operations completed ok","",0);
  966.     fp[n] = NULL;                /* and mark it so */
  967.     iflen = -1;
  968.     return(1);
  969.     }
  970.  
  971.     /* Other kind of file */
  972.  
  973.     if ((fp[n] != stdout) && (fp[n] != stdin))
  974.       x = fclose(fp[n]);
  975.     fp[n] = NULL;
  976.     iflen = -1;                    /* Invalidate file length */
  977.     debug(F101,"  x","",x);
  978.     if (x == EOF)                /* if we got a close error */
  979.       return (-1);
  980.     else
  981.       return (1);
  982. }
  983.  
  984. int
  985. get_subprc_line() {
  986.     struct iosb_struct subiosb;
  987.  
  988.     unsigned int sts;
  989. /*
  990.  * Someone complained that subprocess deletion would hang the Kermit server.
  991.  * This can be triggered by sending something silly like REMOTE HOST STOP/ID=0.
  992.  * If SUPERSAFE is defined we will check to make sure the subprocess still
  993.  * exists before every read from the mailbox. This will slow things down a bit,
  994.  * but should stop the "C-Kermit just dies" reports.
  995.  */
  996. #ifdef    SUPERSAFE
  997.     unsigned short pid;
  998.  
  999.     struct itmlstdef {
  1000.     short int buflen;
  1001.     short int itmcod;
  1002.     char *bufaddr;
  1003.     long int *retlen;
  1004.     };
  1005.     struct itmlstdef itmlst[] = {
  1006.     4, JPI$_PID, (char *)&pid, 0,
  1007.     0, 0, 0, 0
  1008.     };
  1009.     sts = sys$getjpiw(0, &sub_pid, 0, &itmlst, 0, 0, 0);
  1010.  
  1011.     debug(F101,"get_subprc_line sys$getjpiw status", "", sts);
  1012.     if (sts == SS$_NONEXPR)
  1013.     return(-1);
  1014. #endif    /* SUPERSAFE */
  1015.  
  1016.     sts = sys$qiow(QIOW_EFN, mbx_chan, IO$_READVBLK, &subiosb, 0, 0, sub_buf,
  1017.      sizeof(sub_buf), 0, 0, 0, 0);
  1018.  
  1019.     debug(F101,"get_subprc_line sys$qiow status", "", sts);
  1020.     if (sts != SS$_NORMAL)
  1021.     return(-1);
  1022.  
  1023.     debug(F101,"get_subprc_line sys$qiow subiosb.status", "", subiosb.status);
  1024.     if (subiosb.status == SS$_ENDOFFILE)
  1025.     return(-1);
  1026.  
  1027.     if (subiosb.status != SS$_NORMAL)
  1028.     return(-1);
  1029.  
  1030.     sub_buf[subiosb.size] = '\r';
  1031.     sub_buf[subiosb.size + 1] = '\n';
  1032.     sub_buf[subiosb.size + 2] = '\0';
  1033.     sub_count = subiosb.size + 2;
  1034.     sub_ptr = sub_buf;
  1035.  
  1036.     return(0);
  1037. }
  1038.  
  1039. /*  Z C H I N  --  Get a character from the input file.  */
  1040.  
  1041. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  1042.  
  1043. int
  1044. zchin(n,c) int n, *c; {
  1045.     int a;
  1046.  
  1047. #ifdef DEBUG
  1048.     if (chkfn(n) < 1) return(-1);
  1049. #endif
  1050.  
  1051.     if (n == ZIFILE && subprocess_input) {
  1052.     if (--sub_count < 0)
  1053.         if (get_subprc_line()) return(-1);
  1054.     a = *sub_ptr++;
  1055.     } else {
  1056.     a = zminchar();
  1057.     }
  1058.     if (a == EOF) return(-1);
  1059.     *c = (unsigned char)a;
  1060.     return(0);
  1061. }
  1062.  
  1063. /*  Z S I N L  --  Read a line from a file.  */
  1064.  
  1065. /*
  1066.   Writes the line into the address provided by the caller.
  1067.   n is the Kermit "channel number".
  1068.   Writing terminates when newline is encountered, newline is not copied.
  1069.   Writing also terminates upon EOF or if length x is exhausted.
  1070.   Returns 0 on success, -1 on EOF or error.
  1071. */
  1072.  
  1073. int
  1074. zsinl(n,s,x) int n, x; char *s; {
  1075.     int a, z = 0;
  1076.     int old;
  1077.  
  1078.     if (chkfn(n) < 1) {            /* Make sure file is open */
  1079.     return(-1);
  1080.     }
  1081.     a = -1;
  1082.     while (x--) {
  1083.     old = a;            /* Previous character */
  1084.     if (zchin(n,&a) < 0)        /* Read a character from the file */
  1085.       return(-1);            /* Signal EOF if problem */
  1086.     a = a & 0377;
  1087. #ifdef NLCHAR
  1088.     if (a == (char) NLCHAR) break;    /* Single-character line terminator */
  1089. #else
  1090.     if (a == '\r') {
  1091.         continue;
  1092.     }
  1093.     if (old == '\r') {
  1094.         if (a == '\n') break;
  1095.         else *s++ = '\r';
  1096.     }
  1097. #endif /* NLCHAR */
  1098.     *s = a;
  1099.     s++;
  1100.     }
  1101.     *s = '\0';
  1102.     return(z);
  1103. }
  1104.  
  1105. /*  Z I N F I L L  --  Read a line from a file.  */
  1106.  
  1107. /*
  1108.  * (re)fill the buffered file input buffer with data.  All file input
  1109.  * should go through this routine, usually by calling the zminchar()
  1110.  * macro (defined in ckcker.h).
  1111.  */
  1112.  
  1113. int
  1114. zinfill() {
  1115.     char cchar;
  1116.     int linelen;
  1117.  
  1118.     if (subprocess_input) {
  1119.     if (get_subprc_line()) return(-1);
  1120. /*
  1121.  * The size problem should never happen.  sub_buf of a size greater then
  1122.  * 1k is highly unlikely to be needed.
  1123.  */
  1124.     if (INBUFSIZE < SUB_BUF_SIZE) {
  1125.         fprintf(stderr,"zinfill: sub_buf too large for zinbuffer");
  1126.         exit();
  1127.     }
  1128.     zinptr = sub_buf;
  1129.     zincnt = sub_count;
  1130.     } else {
  1131.     if (ifile_bmode != 0) {
  1132.         rab_ifile.rab$l_rop = RAB$M_BIO;    /* block mode I/O */
  1133. #ifdef DYNAMIC
  1134.         rab_ifile.rab$l_ubf = zinbuffer;
  1135. #else
  1136.         rab_ifile.rab$l_ubf = &zinbuffer;
  1137. #endif /* DYNAMIC */
  1138. /*
  1139.   There is a serious flaw here, namely that reading blocks rather than
  1140.   records *includes* the record pad byte (NUL) when the record has an
  1141.   odd length.  All RMS records are stored on even-numbered byte boundaries.
  1142.   Kermit-32 did it right, sigh.  So we need a total rewrite to allow for
  1143.   odd-length records.  Hmmm.. I wonder what the impact on RESEND is...
  1144. */
  1145.         rab_ifile.rab$w_usz = 512;
  1146.         rms_sts = sys$read(&rab_ifile);
  1147.         if (rms_sts == RMS$_EOF)
  1148.         return(-1);            /* end of file */
  1149.         if (rms_sts != RMS$_NORMAL) {
  1150.         debug(F101," zinfill $read failed, status","",rms_sts);
  1151.         return(-1);            /* fatal */
  1152.         }
  1153.         ifile_bcount++;        /* Say another block read */
  1154.         zincnt = 512;
  1155.         zinptr = zinbuffer;
  1156.  
  1157.         if (rab_ifile.rab$l_bkt != 0) { /* If just a file position... */
  1158.         ifile_bcount = rab_ifile.rab$l_bkt; /* update block counter */
  1159.         }
  1160.         if (ifile_bcount == xabfhc_ifile.xab$l_ebk) {
  1161.         if (ifile_bmode == 1)    /* BINARY but not LABELED */
  1162.             zincnt = xabfhc_ifile.xab$w_ffb;
  1163.         }
  1164.         if (rab_ifile.rab$l_bkt != 0) { /* If just a file position... */
  1165.         return(0);            /*...then done                */
  1166.         }
  1167.         zincnt--;            /* one less char in buffer */
  1168.         return((int)(*zinptr++) & 0377); /* because we return the first */
  1169.     }
  1170.     if (fab_ifile.fab$b_rat & FAB$M_FTN) {
  1171. #ifdef DYNAMIC
  1172.         rab_ifile.rab$l_ubf = zinbuffer+2;
  1173. #else
  1174.         rab_ifile.rab$l_ubf = &zinbuffer+2;
  1175. #endif
  1176.         rab_ifile.rab$w_usz = INBUFSIZE-4;    /* space for carriage ctl */
  1177.     } else {
  1178. #ifdef DYNAMIC
  1179.         rab_ifile.rab$l_ubf = zinbuffer;
  1180. #else
  1181.         rab_ifile.rab$l_ubf = &zinbuffer;
  1182. #endif
  1183.         rab_ifile.rab$w_usz = INBUFSIZE-2;    /* space for possible CR/LF */
  1184.     }
  1185.     rab_ifile.rab$l_rop = 0;        /* doing record I/O */
  1186.     rms_sts = sys$get(&rab_ifile);
  1187.     if (rms_sts == RMS$_EOF)
  1188.         return(-1);                /* end of file */
  1189.     if (rms_sts != RMS$_NORMAL) {
  1190.         debug(F101," zinfill $get failed, status","",rms_sts);
  1191.        return(-1);                /* fatal */
  1192.     }
  1193.  
  1194. /*
  1195.  * Do assorted contortions with Fortran carriage control to make it formatted
  1196.  * ASCII instead, since many systems don't know about Fortran format in files.
  1197.  */
  1198.  
  1199.     if (fab_ifile.fab$b_rat & FAB$M_FTN) {
  1200.         linelen = rab_ifile.rab$w_rsz-1;    /* sans control code */
  1201.         cchar = zinbuffer[2];        /* control code */
  1202.         switch (cchar) {
  1203.         case '\0':            /* data<CR> */
  1204.         case '+':
  1205.             zinbuffer[linelen+3] = '\r';/*  insert return */
  1206.             zinptr = zinbuffer+3;
  1207.             zincnt = linelen+1;        /* count it */
  1208.             break;
  1209.         case '$':            /* <LF>data<CR> */
  1210.         case ' ':
  1211.             zinbuffer[2] = '\n';    /*  insert newline */
  1212.             zinbuffer[linelen+3] = '\r';/*  insert return */
  1213.             zinptr = zinbuffer+2;
  1214.             zincnt = linelen+2;        /*  count 'em */
  1215.             break;
  1216.         case '0':            /* <LF><CR><LF>data<CR> */
  1217.             zinbuffer[0] = '\n';    /*  insert 1st newline */
  1218.             zinbuffer[1] = '\r';    /*  insert 1st return */
  1219.             zinbuffer[2] = '\n';    /*  insert 2nd newline */
  1220.             zinbuffer[linelen+3] = '\r';/*  insert 2nd return */
  1221.             zinptr = zinbuffer;
  1222.             zincnt = linelen+4;        /*  count 'em */
  1223.             break;
  1224.         case '1':            /* <FF>data<CR> */
  1225.             zinbuffer[2] = '\f';    /*  insert formfeed */
  1226.             zinbuffer[linelen+3] = '\r';/*  insert return */
  1227.             zinptr = zinbuffer+2;
  1228.             zincnt = linelen+2;        /*  count 'em */
  1229.             break;
  1230.         default:            /* <LF>data<CR> */
  1231.             zinbuffer[2] = '\n';    /*  insert newline */
  1232.             zinbuffer[linelen+3] = '\r';/*  insert return */
  1233.             zinptr = zinbuffer+2;
  1234.             zincnt = linelen+2;        /*  count 'em */
  1235.             break;
  1236.         }
  1237.     } else {
  1238.         zincnt = rab_ifile.rab$w_rsz;
  1239.         zinptr = zinbuffer;            /* reset pointer */
  1240.     }
  1241.  
  1242. /*
  1243.  * Here we see if we need to insert CR/LF pairs at the record boundary. For
  1244.  * the moment, we will add them if the file has "carriage return carriage
  1245.  * control" when looked at by a DIRECTORY command. As of edit 036 we also do
  1246.  * this for "print file carriage control" files. I'm open to comments de-
  1247.  * scribing cases where this doesn't work...
  1248.  */
  1249.  
  1250.     if (fab_ifile.fab$b_rat & (FAB$M_CR | FAB$M_PRN)) {
  1251.         zinbuffer[zincnt] = '\r';
  1252.         zinbuffer[zincnt + 1] = '\n';
  1253.         zincnt += 2;
  1254.     }
  1255.     }
  1256.     zincnt--;            /* one less char in buffer */
  1257.     return((int)(*zinptr++) & 0377); /* because we return the first */
  1258. }
  1259.  
  1260.  
  1261. /*  Z F S E E K -- Seek to a given position with an input file */
  1262. /*                 Assumes block-mode I/O being used           */
  1263.  
  1264. int
  1265. zfseek(long pos) {
  1266.     long offset;
  1267.  
  1268.     rab_ifile.rab$l_bkt = (unsigned long) pos >> 9; /* Get block number */
  1269.     rab_ifile.rab$l_bkt++;        /* VBN's are 1-based */
  1270.     offset = (unsigned long) pos & 511;    /* Get offset with block */
  1271.     if (zinfill() != 0) {        /* Read in the block */
  1272.     rab_ifile.rab$l_bkt = 0;    /* Sequentially from now on */
  1273.     return(-1);
  1274.     }
  1275.     rab_ifile.rab$l_bkt = 0;        /* Sequentially from now on */
  1276.     if (offset != 0) {            /* if not block boundary... */
  1277.     zincnt = zincnt - offset;    /* ...adjust count and pointer */
  1278.     zinptr = zinptr + offset;
  1279.     }
  1280.     return(0);
  1281. }
  1282.  
  1283. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  1284.  
  1285. int
  1286. zsout(n,s) int n; char *s; {
  1287. #ifdef DEBUG
  1288.     if (chkfn(n) < 1) return(-1);
  1289. #endif
  1290.     fputs(s, fp[n]);            /* Don't use fprintf here MM */
  1291.     return(0);
  1292. }
  1293.  
  1294.  
  1295. /*  Z S O U T L  --  Write string to file, with line terminator, buffered.  */
  1296.  
  1297. int
  1298. zsoutl(n,s) int n; char *s; {
  1299. #ifdef DEBUG
  1300.     if (chkfn(n) < 1) return(-1);
  1301. #endif
  1302.     fputs(s, fp[n]);            /* Don't use fprintf MM */
  1303.     putc('\n', fp[n]);
  1304.     return(0);
  1305. }
  1306.  
  1307.  
  1308. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  1309.  
  1310. int
  1311. zsoutx(n,s,x) int n, x; char *s; {
  1312. #ifdef DEBUG
  1313.     if (chkfn(n) < 1) return(-1);
  1314. #endif
  1315.     return(write(fileno(fp[n]),s,x));
  1316. }
  1317.  
  1318.  
  1319. /*  Z C H O U T  --  Add a character to the given file.  */
  1320.  
  1321. int
  1322. #ifdef CK_ANSIC
  1323. zchout(register int n, char c)
  1324. #else
  1325. zchout(n,c) register int n; char c;
  1326. #endif /* CK_ANSIC */
  1327. /* zchout */ {
  1328. #ifdef DEBUG
  1329.     if (chkfn(n) < 1) return(-1);
  1330. #endif
  1331.     if (n == ZSFILE) {
  1332.         return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */
  1333.     } else {
  1334.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  1335.       return(ferror(fp[n]) ? -1 : 0); /* Check to make sure */
  1336.     else                /* Otherwise... */
  1337.       return(0);            /* There was no error. */
  1338.     }
  1339. }
  1340.  
  1341. /*  Z O U T D U M P  --  dump buffered output characters to file.  */
  1342.  
  1343. /* Buffered file output, buffer dump */
  1344.  
  1345. /*
  1346.  * No, this isn't an entry in the 199x Obfuscated C programming contest, nor
  1347.  * did we get it at an all-night convenience store. VMS requires that stream
  1348.  * format files be written as records, so we have to do _lots_ of contortion
  1349.  * to make sure we write whole lines as records. Not pretty.
  1350.  */
  1351.  
  1352. int
  1353. zoutdump() {
  1354.     int ocnt;
  1355.     int wrote_one_line = 0;
  1356.     CHAR *optr, *srcptr, *endptr;
  1357.     char csave;
  1358.  
  1359.     debug(F101," zoutdump zoutcnt","",zoutcnt);
  1360.     debug(F101," zoutdump ofile_bmode","",ofile_bmode);
  1361. /*
  1362.  * Well, this could be to the console. If it is, chop it into itty-bitty parts
  1363.  * (the VMS CRTL can't handle a %s spec bigger than 512 bytes) and print it.
  1364.  */
  1365.     if (cflag == 1) {            /* If we're dumping to console */
  1366.     endptr = zoutbuffer + zoutcnt;
  1367.     for (optr = zoutbuffer; optr < endptr; optr += 511) {
  1368.         if (optr+511 < endptr) {    /* More than 511, break up */
  1369.         csave = *(optr+511);
  1370.         *(optr+511) = '\0';
  1371.         printf("%s", optr);
  1372.         *(optr+511) = csave;
  1373.         }
  1374.         else {
  1375.         *endptr = '\0';        /* Make sure null-terminated */
  1376.         printf("%s", optr);
  1377.         }
  1378.     }
  1379.     zoutcnt = 0;
  1380.     zoutptr = zoutbuffer;
  1381.     return(0);
  1382.     }
  1383.  
  1384. /*
  1385.  * Do we need to processed TYPE LABELED contortions?
  1386.  */
  1387.  
  1388.     if (ofile_bmode == XYFT_L) {    /* Is it labeled? */
  1389.     if (ofile_lblproc == 0)    {    /* I've never gone this way before? */
  1390.         rms_sts = do_label_recv();    /* Beyond revolving rainbow door... */
  1391.         if (rms_sts == -1)
  1392.         return(-1);        /* Got a hard error in label proc. */
  1393.         if (rms_sts == 1 && ofile_dump != 1)
  1394.         return(0);        /* Exit so we can fill up the buffer */
  1395.     }
  1396.     }
  1397. /*
  1398.  * Well, we could be lucky...
  1399.  */
  1400.     if (zoutcnt == 0)
  1401.     return(0);
  1402. /*
  1403.  * Oh well. See if doing binary - that's easy...
  1404.  */
  1405.     if (ofile_bmode) {
  1406.     if (zoutcnt == OBUFSIZE) {
  1407. #ifdef DYNAMIC
  1408.         rab_ofile.rab$l_rbf = zoutbuffer;
  1409. #else
  1410.         rab_ofile.rab$l_rbf = &zoutbuffer;
  1411. #endif /* DYNAMIC */
  1412.         rab_ofile.rab$w_rsz = OBUFSIZE;
  1413.         if (ofile_ffb != -1 && ofile_dump == 1) {
  1414.         /*
  1415.          * Only do this when doing _last_ file segment.
  1416.          */
  1417.         xabfhc_ofile.xab$w_ffb = ofile_ffb;
  1418.         if (ofile_ffb)
  1419.             rab_ofile.rab$w_rsz -= (512 - ofile_ffb);
  1420.         debug(F101," zoutdump ofile_ffb","",(int)ofile_ffb);
  1421.         debug(F101," zoutdump rab$w_rsz","",rab_ofile.rab$w_rsz);
  1422.         }
  1423.         rms_sts = sys$write(&rab_ofile);
  1424.         if (rms_sts != RMS$_NORMAL) {
  1425.         debug(F101," zoutdump $write failed, status","",rms_sts);
  1426.         return(-1);
  1427.         }
  1428.     } else {
  1429. #ifdef DYNAMIC
  1430.         rab_ofile.rab$l_rbf = zoutbuffer;
  1431. #else
  1432.         rab_ofile.rab$l_rbf = &zoutbuffer;
  1433. #endif
  1434.         rab_ofile.rab$w_rsz = zoutcnt;
  1435.         xabfhc_ofile.xab$w_ffb = (zoutcnt & 511)+1;
  1436.         if (ofile_ffb != -1) {
  1437.         xabfhc_ofile.xab$w_ffb = ofile_ffb;
  1438.         if (ofile_ffb)
  1439.             rab_ofile.rab$w_rsz -= (512 - ofile_ffb);
  1440.         debug(F101," zoutdump ofile_ffb","",(int)ofile_ffb);
  1441.         debug(F101," zoutdump rab$w_rsz","",rab_ofile.rab$w_rsz);
  1442.         }
  1443.         rms_sts = sys$write(&rab_ofile);
  1444.         if (rms_sts != RMS$_NORMAL) {
  1445.         debug(F101," zoutdump $write failed, status","",rms_sts);
  1446.         return(-1);
  1447.         }
  1448.     }
  1449.     debug(F100," zoutdump RMS operations completed ok","",0);
  1450.     zoutcnt = 0;
  1451.     zoutptr = zoutbuffer;
  1452.     return(0);
  1453.     }
  1454.  
  1455. /*
  1456.  * Must be ASCII. This is harder, and weirder... It's actually easier than
  1457.  * it looks, but there's (unfortunately) no really easy way to _implement_
  1458.  * it. (sigh, whimper, groan)
  1459.  */
  1460.  
  1461.     srcptr = zoutbuffer;            /* Points to first line in buffer */
  1462.     endptr = zoutbuffer + zoutcnt;  /* Points to location after last char */
  1463. zoutdump_ascii:
  1464.     /* Scan through buffer until we find a CR or we run out of chars */
  1465.     for (optr = srcptr; optr < endptr; optr++) if (*optr == CR) break;
  1466.  
  1467.     /* If there are at least 2 chars left in the buffer when we stop   */
  1468.     /* scanning, then it is assumed the above loop terminated because  */
  1469.     /* it found the CR and that both the CR and LF are present in the  */
  1470.     /* buffer (situation normal.                                       */
  1471.     /* If there are not 2 chars left in the buffer, we have one of two */
  1472.     /* cases which we treat identically:                               */
  1473.     /*  1) If there are 0 chars left in the buffer, then the line's    */
  1474.     /*     terminating CR LF are yet to come. So... we copy the data   */
  1475.     /*     to the front of the buffer and exit (next time it should be */
  1476.     /*     there.)                                                     */
  1477.     /*  2) If there is one char left in the buffer, we have the case of*/
  1478.     /*     a line with the CR but no LF present. So... do the same     */
  1479.     /*     because the LF will be coming next time.                    */
  1480.     if (optr+2 > endptr) {            /* drat! ran off the end */
  1481.     if (ofile_dump && (srcptr == endptr)) {
  1482.         /* If the beginning and end ptrs are the same, then there the  */
  1483.         /* is empty. Good news, 'cause we're clsoing up.               */
  1484.         zoutcnt = 0;        /* No looping, please. */
  1485.         zoutptr = zoutbuffer;
  1486.     }
  1487.     else if (ofile_dump) {        /* but it's cool, we're closing up */
  1488.         /* Oops, we've got a line with no LF and maybe no CR. Well     */
  1489.         /* write it out and exit abnormally.                           */
  1490.         rab_ofile.rab$l_rbf = srcptr;
  1491.         rab_ofile.rab$w_rsz = optr-srcptr;
  1492.         rms_sts = sys$put(&rab_ofile);
  1493.         zoutcnt = 0;
  1494.         zoutptr = zoutbuffer;
  1495.         if (rms_sts != RMS$_NORMAL) {
  1496.         debug(F101, " zoutdump $put failed, status","",rms_sts);
  1497.         return(-1);
  1498.         }
  1499.     } else if (wrote_one_line) {    /* it's still cool, we did one... */
  1500.         zoutcnt = optr - srcptr;    /* number of chars left */
  1501.         if (optr < endptr) zoutcnt++; /*[jah083] including CR if present */
  1502.         if (zoutcnt) memmove(zoutbuffer, srcptr, zoutcnt);
  1503.             /* Move'em to front of buffer*/
  1504.         zoutptr = zoutbuffer+zoutcnt;
  1505.     } else {            /* WRONG!!! */
  1506.         /* We've got a buffer full of chars with no LF (it may or may  */
  1507.         /* not have a terminating CR. In either case its just plain too*/
  1508.         /* long. I suppose we could check here for the optr+1 == endptr*/
  1509.         /* which indicates that there was a CR but no LF so we could   */
  1510.         /* issue a "line barely too long", but, is it useful?          */
  1511.         debug(F100, "zoutdump: line too long","",0);
  1512.         zoutcnt = 0;        /* No looping, please. */
  1513.         zoutptr = zoutbuffer;
  1514.         return(-1);
  1515.     }
  1516.     debug(F101, " zoutdump exiting, zoutcnt","",zoutcnt);
  1517.     return(0);
  1518.     }
  1519.  
  1520.     /* We now have a line that we can write, so... */
  1521.  
  1522.     rab_ofile.rab$l_rbf = srcptr;
  1523.     rab_ofile.rab$w_rsz = optr-srcptr;
  1524.     rms_sts = sys$put(&rab_ofile);
  1525.     if (rms_sts != RMS$_NORMAL) {
  1526.     debug(F101, " zoutdump $put failed, status","",rms_sts);
  1527.     return(-1);
  1528.     }
  1529.     srcptr = optr + 2;        /* Account for CR, LF */
  1530.     wrote_one_line = 1;
  1531.     goto zoutdump_ascii;
  1532. }
  1533.  
  1534. /*  C H K F N  --  Internal function to verify file number is ok.  */
  1535.  
  1536. /*
  1537.  Returns:
  1538.   -1: File number n is out of range
  1539.    0: n is in range, but file is not open
  1540.    1: n in range and file is open
  1541. */
  1542.  
  1543. int
  1544. chkfn(n) int n; {
  1545.     switch (n) {
  1546.     case ZCTERM:
  1547.     case ZSTDIO:
  1548.     case ZIFILE:
  1549.     case ZOFILE:
  1550.     case ZDFILE:
  1551.     case ZTFILE:
  1552.     case ZPFILE:
  1553.     case ZSFILE:
  1554.         break;
  1555.     case ZSYSFN:            /* System functions */
  1556.         return(0);
  1557.     case ZRFILE:            /* READ and WRITE files */
  1558.     case ZWFILE:
  1559.         case ZMFILE:
  1560.         break;
  1561.     default:
  1562.         debug(F101,"chkfn: file number out of range","",n);
  1563.         fprintf(stderr,"?File number out of range - %d\n",n);
  1564.         return(-1);
  1565.     }
  1566.     return( (fp[n] == NULL) ? 0 : 1 );
  1567. }
  1568.  
  1569. /*  Z C H K I  --  Check if input file exists and is readable.  */
  1570.  
  1571. /*
  1572.   Returns:
  1573.    >= 0 if the file can be read (returns the size).
  1574.      -1 if file doesn't exist or can't be accessed,
  1575.      -2 if file exists but is not readable (e.g. a directory file).
  1576.      -3 if file exists but protected against read access.
  1577. */
  1578.  
  1579. long
  1580. zchki(name) char *name; {
  1581.     struct stat buf;
  1582.     int x; long y;
  1583.     struct FAB fab_chki;
  1584.     struct XABFHC xabfhc_chki;
  1585.  
  1586. /* This is _really_ bad. But there's a fundamental assumption in the upper
  1587.  * levels that one can call zchki() without any context to validate file-
  1588.  * names, directory names, etc. which would be painful (to the other imple-
  1589.  * mentations) to change. So, if we get an argument which ends in ':', '>',
  1590.  * or ']', we'll return an immediate OK with a size of 0. Bad directory
  1591.  * names will be caught in zchdir anyway. This has the nice side-effect that
  1592.  * saying (for example) GET dir-spec will implicitly get all files in that
  1593.  * directory. Not bad for a total kludge, huh?
  1594.  */
  1595.     x = strlen(name);
  1596.     if (name[x-1] == ':')
  1597.     return(0);
  1598.     if (name[x-1] == ']')
  1599.     return(0);
  1600.     if (name[x-1] == '>')
  1601.     return(0);
  1602.  
  1603.     fab_chki = cc$rms_fab;
  1604.     fab_chki.fab$b_fac = FAB$M_BIO;
  1605.     fab_chki.fab$l_fna = name;
  1606.     fab_chki.fab$b_fns = strlen(name);
  1607.     fab_chki.fab$l_xab = (char *)&xabfhc_chki;
  1608.     xabfhc_chki = cc$rms_xabfhc;
  1609.     rms_sts = sys$open(&fab_chki);
  1610.     if (rms_sts == RMS$_PRV)            /* No privs */
  1611.     return(-3);
  1612.     if (rms_sts != RMS$_NORMAL) {
  1613.     debug(F101," zchki $open failed, status","",rms_sts);
  1614.     return(-1);
  1615.     }
  1616.     iflen = ((xabfhc_chki.xab$l_ebk-1)*512)+xabfhc_chki.xab$w_ffb;
  1617.  
  1618.     rms_sts = sys$close(&fab_chki);
  1619.     if (rms_sts != RMS$_NORMAL) {
  1620.     debug(F101," zchki $close failed, status","",rms_sts);
  1621.     return(-1);
  1622.     }
  1623.     strcpy(nambuf,name);        /* preserve name */
  1624.     debug(F111," zchki access ok:",name,(int) iflen); /* Yes */
  1625.     return( (iflen > -1) ? iflen : 0 );
  1626. }
  1627.  
  1628. /*  Z C H K O  --  Check if output file can be created.  */
  1629.  
  1630. /*
  1631.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  1632. */
  1633. int
  1634. zchko(name) char *name; {
  1635.     return(0);                /* Always creates new version */
  1636. }
  1637.  
  1638. /*  Z C H K S P A  --  Check if there is enough space to store the file.  */
  1639.  
  1640. /*
  1641.  Call with file specification f, size n in bytes.
  1642.  Returns -1 on error, 0 if not enough space, 1 if enough space.
  1643. */
  1644.  
  1645. int
  1646. zchkspa(f,n) char *f; long n; {
  1647.  
  1648. /*
  1649.  * This is complicated. The user could have specified an explicit path when
  1650.  * sending the file, or could have done a CWD, or could be using the default
  1651.  * directory. If not the latter, the path may not even be a disk device, as
  1652.  * CWD LPA0: is perfect legal for uploading to the lineprinter. After that,
  1653.  * if it's a disk, we should check the user's quota.  However, the user may
  1654.  * have SYSPRV, EXQUOTA, BYPASS, or maybe even GRPPRV, and it would be hard
  1655.  * to  properly check for all these cases.  So, if the file will fit on the
  1656.  * disk, we'll accept it.
  1657.  */
  1658.  
  1659.     char   *zgtdir();
  1660.  
  1661.     struct itmlstdef {
  1662.     short int buflen;
  1663.     short int itmcod;
  1664.     char *bufaddr;
  1665.     long int *retlen;
  1666.     };
  1667.  
  1668.     static char device[64];
  1669.  
  1670.     struct dsc$descriptor_s
  1671.     dev_desc = {sizeof(device), DSC$K_DTYPE_T, DSC$K_CLASS_S,
  1672.               (char *)&device};
  1673.     unsigned long freeblocks, freelength, devclass, classlength, fileblocks;
  1674.  
  1675.     struct itmlstdef itmlst[] =
  1676.     {4,DVI$_FREEBLOCKS,0,0,4,DVI$_DEVCLASS,0,0,0,0,0,0};
  1677.  
  1678.     int rms_sts;
  1679.  
  1680. /* First, figure out the device we're interested in */
  1681.  
  1682.     strcpy(device, zgtdir());            /* Handles default or CWD */
  1683.  
  1684.     if (strchr(f, ':'))                /* If user specified path */
  1685.     strncpy(device, f, 63);
  1686.  
  1687.     debug(F110," zchkspa target device is ",device,0);
  1688.  
  1689. /* Next, ask for free block count and device type (disk vs. non-disk) */
  1690.  
  1691.     itmlst[0].bufaddr = (char *)&freeblocks;
  1692.     itmlst[0].retlen = &freelength;
  1693.     itmlst[1].bufaddr = (char *)&devclass;
  1694.     itmlst[1].retlen = &classlength;
  1695.  
  1696.     rms_sts = sys$getdviw(0,0,&dev_desc,&itmlst,0,0,0,0);
  1697.  
  1698.     debug(F101," zchkspa $getdvi returned rms_sts","",rms_sts);
  1699.  
  1700.     if (devclass != DC$_DISK)
  1701.     return(1);                /* assume space if not disk */
  1702.  
  1703.     if (rms_sts != SS$_NORMAL)
  1704.     return(1);                /* assume free space if err */
  1705.  
  1706.     debug(F101," zchkspa $getdvi returned freeblocks","",freeblocks);
  1707.  
  1708. /* Pad file size if it's a text file */
  1709.  
  1710.     if (ofile_bmode == XYFT_T)
  1711.     n += (n/40) * 3;
  1712.  
  1713.     fileblocks = n / 512 + 1;            /* compute file size in blks */
  1714.                         /* we may want some fuzz */
  1715.     if (fileblocks >= freeblocks)
  1716.     return(0);                /* Won't fit */
  1717.     else
  1718.     return(1);                /* Will fit */
  1719. }
  1720.  
  1721. /*  Z D E L E T  --  Delete the named file.  */
  1722.  
  1723. int
  1724. zdelet(name) char *name; {
  1725.     return(delete(name));
  1726. }
  1727.  
  1728. /*  Z R T O L  --  Convert remote filename into local form.  */
  1729.  
  1730. VOID
  1731. zrtol(name,name2) char *name, *name2; {
  1732.     int count = 9, vflag = 0;
  1733.     char *cp, c;
  1734.     static char *spcl_set = "_-$[]<>:.\";";
  1735.  
  1736.     for (cp=name2; c = *name; name++) {
  1737.     if (islower(c)) c = toupper(c);
  1738.     if (!isalnum(c) &&
  1739.         !strchr(spcl_set,c)) c = 'X';
  1740.     *cp++ = c;
  1741.     }
  1742.     *cp = '\0';                /* End of name */
  1743.     debug(F110," zrtol: ",name2,0);
  1744. }
  1745.  
  1746. /*  Z L T O R  --  Convert filename from local format to common form.  */
  1747.  
  1748. VOID
  1749. zltor(name,name2) char *name, *name2; {
  1750.     char *cp, *pp;
  1751.  
  1752. /*
  1753.  * Copy name to output string
  1754.  */
  1755.  
  1756.     strcpy(name2,name);
  1757.  
  1758. /*
  1759.  * Parse the filename and type, with the default filename of "X"
  1760.  */
  1761.  
  1762.     parse_fname(name2, 100, "X", PARSE_NAME|PARSE_TYPE);
  1763.     debug(F110," zltor: ",name2,0);
  1764. }
  1765.  
  1766. /*  Z C H D I R  --  Change directory.  */
  1767.  
  1768. int
  1769. zchdir(dirnam) char *dirnam; {
  1770.  
  1771.     char   *zgtdir();
  1772.     char   dir_buff[NAM$C_MAXRSS];
  1773.     int    status;
  1774.  
  1775.     if (*dirnam == '\0')
  1776.         strcpy(dirnam,getenv("HOME"));    /* default to current dir */
  1777.  
  1778.     status = chdir(dirnam);        /* change first in parent proc */
  1779.     return(status == 0);
  1780. }
  1781.  
  1782. /*  Z H O M E  --  Return pointer to user's home directory.  */
  1783.  
  1784. char *
  1785. zhome() {
  1786.     return(getenv("HOME"));
  1787. }
  1788.  
  1789. /*  Z G T D I R  --  Return pointer to user's current directory.  */
  1790.  
  1791. char *
  1792. zgtdir() {
  1793. #ifdef VMS_V40
  1794. #define    OLD_VMS
  1795. #endif
  1796. #ifdef VMS_V42
  1797. #define    OLD_VMS
  1798. #endif
  1799. #ifdef VMS_V44
  1800. #define    OLD_VMS
  1801. #endif
  1802. #ifdef VAXC023
  1803. #define    OLD_VMS
  1804. #endif
  1805. #ifdef VAXC024
  1806. #define    OLD_VMS
  1807. #endif
  1808.  
  1809. #ifdef OLD_VMS
  1810.     static char *gtdir_buf = 0;
  1811.     static char sysdisk[] = "SYS$DISK";
  1812.     char tmp_buf[NAM$C_MAXRSS+1];
  1813.     struct dsc$descriptor_s
  1814.     tmp_buf_dsc = {sizeof(tmp_buf),DSC$K_DTYPE_T,DSC$K_CLASS_S,&tmp_buf},
  1815.     sysdisk_dsc = {sizeof(sysdisk)-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,&sysdisk};
  1816.     unsigned short int buf_len;
  1817.  
  1818. /*
  1819.  * Allocate buffer dynamically, first time through.  This makes the image
  1820.  * smaller.
  1821.  */
  1822.  
  1823.     if (!gtdir_buf) gtdir_buf = malloc(NAM$C_MAXRSS+1);
  1824.  
  1825. /*
  1826.  * Translate device name.
  1827.  */
  1828.  
  1829.     LIB$SYS_TRNLOG(    &sysdisk_dsc,
  1830.             &buf_len,
  1831.             &tmp_buf_dsc,
  1832.             0,
  1833.             0,
  1834.             0);
  1835.     tmp_buf[buf_len] = '\0';
  1836.     strcpy(gtdir_buf,tmp_buf);
  1837.  
  1838. /*
  1839.  * Get directory name.
  1840.  */
  1841.  
  1842.     sys$setddir(    0,      /* New dir addr */
  1843.             &buf_len, /* length addr */
  1844.             &tmp_buf_dsc);
  1845.     tmp_buf[buf_len] = '\0';
  1846.     strcat(gtdir_buf,tmp_buf);
  1847.  
  1848.     return(gtdir_buf);  /* Can't seem to make LINK find getcwd()... */
  1849.             /* (wbader: removed &) */
  1850. #else
  1851.     char *getcwd();
  1852.     char *buf;
  1853.  
  1854.     buf = cwdbuf;
  1855.     return(getcwd(buf,100));
  1856. #endif
  1857. }
  1858.  
  1859. /*  Z X C M D  --  Run a system command so its output can be read as a file. */
  1860.  
  1861. int
  1862. zxcmd(filnum, comand) int filnum; char *comand; {
  1863.     char mbxnam[21], inpchan[6] = "NLA0:";
  1864.     unsigned long sts, pid;
  1865.     int one=1;
  1866.  
  1867.     struct dsc$descriptor_s
  1868.     mbx_desc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
  1869.     cmd_line = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
  1870.     inp_desc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
  1871.  
  1872.     struct itmlstdef {
  1873.     short int buflen;
  1874.     short int itmcod;
  1875.     char *bufaddr;
  1876.     long int *retlen;
  1877.     };
  1878.  
  1879.     struct itmlstdef itmlst[] = {
  1880.     4, JPI$_PID, (char *)&pid, 0,
  1881.     0, 0, 0, 0
  1882.     };
  1883.  
  1884.     debug(F101,"zxcmd filnum", "", filnum);
  1885.     if (filnum != ZIFILE && filnum != ZRFILE)
  1886.     return(0);
  1887.  
  1888.     sts = sys$getjpiw(0, 0, 0, &itmlst, 0, 0, 0);
  1889.  
  1890.     debug(F101,"zxcmd sys$getjpiw status", "", sts);
  1891.     if (sts != SS$_NORMAL)
  1892.     return(0);
  1893.  
  1894.     sprintf(mbxnam,"KERMIT$MBX_%08X", pid);
  1895.     debug(F110,"zxcmd mailbox logical", mbxnam, 0);
  1896.  
  1897.     mbx_desc.dsc$w_length = strlen(mbxnam);
  1898.     mbx_desc.dsc$a_pointer = mbxnam;
  1899.  
  1900.     sts = sys$crembx(0, &mbx_chan, SUB_BUF_SIZE, 0, 0, 0, &mbx_desc, 0);
  1901.  
  1902.     debug(F101,"zxcmd sys$crembx status", "", sts);
  1903.     if (sts != SS$_NORMAL)
  1904.     return(0);
  1905.  
  1906.     debug(F101,"zxcmd sys$crembx mbx_chan", "", mbx_chan);
  1907.  
  1908.     strcat(mbxnam, ":");
  1909.     mbx_desc.dsc$w_length++;
  1910.  
  1911.     cmd_line.dsc$w_length = strlen(comand);
  1912.     cmd_line.dsc$a_pointer = comand;
  1913.  
  1914.     inp_desc.dsc$w_length = strlen(inpchan);
  1915.     inp_desc.dsc$a_pointer = inpchan;
  1916.  
  1917.     sts = lib$spawn(&cmd_line, &inp_desc, &mbx_desc, &one, 0, &sub_pid,
  1918.             0, 0, 0, &mbx_chan);
  1919.  
  1920.     debug(F101,"zxcmd lib$spawn status", "", sts);
  1921.     if (sts != SS$_NORMAL)
  1922.     return(0);
  1923.  
  1924.     subprocess_input = 1;
  1925.     sub_count = 0;
  1926.     fp[filnum] = fopen("NLA0:","r");    /* It wants a fp, give it one */
  1927.     debug(F101,"zxcmd fp[filnum]", "", fp[filnum]);
  1928.     fp[ZSYSFN] = fp[filnum];        /* Set ZSYSFN too, so we remember */
  1929.     return(1);
  1930. }
  1931.  
  1932. /*  Z C L O S F  - close the suprocess output file.  */
  1933.  
  1934. int
  1935. zclosf(filnum) int filnum; {
  1936.     unsigned long sts;
  1937.  
  1938.     if (subprocess_input != 0) {
  1939.     sts = sys$delprc(&sub_pid, 0);
  1940.  
  1941.     debug(F101,"zclosf sys$delprc status", "", sts);
  1942.  
  1943.     sts = sys$delmbx(mbx_chan);
  1944.  
  1945.     debug(F101,"zclosf sys$delmbx status", "", sts);
  1946.  
  1947.     sts = sys$dassgn(mbx_chan);
  1948.  
  1949.     debug(F101,"zclosf sys$dassgn status", "", sts);
  1950.  
  1951.     sub_ptr = sub_buf;            /* flush remaining data */
  1952.     sub_count = 1;
  1953.     *sub_buf = '\0';
  1954.     zincnt = 0;
  1955.  
  1956.     fclose(fp[filnum]);            /* Close the place-holders */
  1957.     fp[filnum] = fp[ZSYSFN] = NULL;
  1958.     }
  1959.     subprocess_input = 0;            /* Say we're done */
  1960.     return(1);
  1961. }
  1962.  
  1963. /*  Z X P A N D  --  Expand a wildcard string into an array of strings.  */
  1964.  
  1965. /*
  1966.   Returns the number of files that match fn1, with data structures set up
  1967.   so that first file (if any) will be returned by the next znext() call.
  1968. */
  1969. int
  1970. zxpand(fn) char *fn; {
  1971.     if (strlen(fn) == 0)        /* Nothing asked for, */
  1972.       return(0);            /* nothing returned. */
  1973.     fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  1974.     if (fcount > 0) {
  1975.     mtchptr = mtchs;        /* Save pointer for next. */
  1976.     debug(F111," zxpand",mtchs[0],fcount);
  1977.     }
  1978.     return(fcount);
  1979. }
  1980.  
  1981. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  1982.  
  1983. /*
  1984.  Returns >0 if there's another file, with its name copied into the arg string,
  1985.  or 0 if no more files in list.
  1986. */
  1987. int
  1988. znext(fn) char *fn; {
  1989.  
  1990.     if (fcount-- > 0) strcpy(fn,*mtchptr++);
  1991.     else *fn = '\0';
  1992.     debug(F111," znext",fn,fcount+1);
  1993.     return(fcount+1);
  1994. }
  1995.  
  1996. /*  Z N E W N  --  Make a new name for the given file.  */
  1997.  
  1998. VOID
  1999. znewn(fn,s) char *fn, **s; {
  2000.     static char buf[NAM$C_MAXRSS];
  2001.  
  2002.     strcpy(buf, fn);            /* Version numbers are handled by OS */
  2003.     *s = buf;
  2004. }
  2005.  
  2006. /*
  2007.  * fgen:
  2008.  *  This is the actual name generator.  It is passed a string,
  2009.  *  possibly containing wildcards, and an array of character pointers.
  2010.  *  It finds all the matching filenames and stores them into the array.
  2011.  *  The returned strings are allocated from a static buffer local to
  2012.  *  this module (so the caller doesn't have to worry about deallocating
  2013.  *  them); this means that successive calls to fgen will wipe out
  2014.  *  the results of previous calls.  This isn't a problem here
  2015.  *  because we process one wildcard string at a time.
  2016.  *
  2017.  * Input: a wildcard string, an array to write names to, the
  2018.  *        length of the array.
  2019.  * Returns: the number of matches.  The array is filled with filenames
  2020.  *          that matched the pattern.  If there wasn't enough room in the
  2021.  *        array, -1 is returned.
  2022.  */
  2023. int
  2024. fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
  2025.     struct dsc$descriptor_s
  2026.     file_spec = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
  2027.     result = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0},
  2028.     deflt = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  2029.     unsigned long context = 0, status;
  2030.     int count = 0;
  2031.     char *def_str = "*.*";
  2032.  
  2033.     file_spec.dsc$w_length  = strlen(pat);
  2034.     file_spec.dsc$a_pointer = pat;
  2035.  
  2036.     deflt.dsc$w_length  = sizeof(def_str)-1;
  2037.     deflt.dsc$a_pointer = def_str;
  2038.  
  2039.     while (count < len
  2040.        && (status = LIB$FIND_FILE(&file_spec, &result, &context, &deflt))
  2041.         == RMS$_NORMAL) {
  2042.         resarry[count] = malloc(result.dsc$w_length + 1);
  2043.     strncpy(resarry[count], result.dsc$a_pointer, result.dsc$w_length);
  2044.     resarry[count][result.dsc$w_length] = '\0';
  2045.     count++;
  2046.     }
  2047. #ifdef DVI$_ALT_HOST_TYPE
  2048.     LIB$FIND_FILE_END(&context);    /* Only on V4 and later */
  2049. #endif
  2050.     LIB$SFREE1_DD(&result);
  2051.     if (status == RMS$_FNF) return((count <= len) ? 0 : -1);
  2052.     if (status == RMS$_NMF) return(count);
  2053.     /* Bernd Onasch says that VMS sometimes returns RMS$_NORMAL here, so... */
  2054.     if (status == RMS$_NORMAL) return(count);
  2055.     /* Some other status.  Return 0. */
  2056.     /* Improve this later based on results from following debug stmt. */
  2057.     debug(F101,"fgen unexpected failure status","",status);
  2058.     return(0);
  2059. }
  2060.  
  2061. /*  Z R E N A M E  --  Rename a file.  */
  2062.  
  2063. /*  Call with old and new names */
  2064. /*  Returns 0 on success, -1 on failure. */
  2065. int
  2066. zrename(old,new) char *old, *new; {
  2067.     int sts;
  2068.  
  2069.     sts = rename(old,new);
  2070.  
  2071.     return((sts ? -1 : 0));
  2072. }
  2073.  
  2074. /*  Z C F D A T  --  Return a file's modification time.  */
  2075.  
  2076. char *
  2077. zfcdat(name) char *name; {
  2078. /*
  2079.   Returns modification date/time of file whose name is given in the argument
  2080.   string.  Return value is a pointer to a string of the form:
  2081.   
  2082.     yyyymmdd hh:mm:ss
  2083.  
  2084.   for example 19931231 23:59:59, which represents the local time (no timezone
  2085.   or daylight savings time finagling required).  Returns an empty string ("")
  2086.   on failure.  The text pointed to by the string pointer is in a static
  2087.   buffer, and so should be copied to a safe place by the caller before any
  2088.   subsequent calls to this function.
  2089. */
  2090.  
  2091. /*
  2092.   Contributed by William Bader, 9 Nov 93, based on UNIX version: "It would
  2093.   probably be possible to get the date by opening the file and requesting a
  2094.   NAM block like ckvfio.c does, but stat seems to do the trick."
  2095. */
  2096.     struct stat statbuf;
  2097.     struct tm *tm;
  2098.     static char datebuf[20];
  2099.  
  2100.     datebuf[0] = '\0';
  2101.  
  2102.     if (name &&
  2103.         *name &&
  2104.         stat(name,&statbuf) != -1 &&
  2105.         (tm = localtime(&statbuf.st_mtime)))
  2106.       sprintf(datebuf, "%04d%02d%02d %02d:%02d:%02d",
  2107.         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  2108.         tm->tm_hour, tm->tm_min, tm->tm_sec);
  2109.  
  2110.     return(datebuf);
  2111. }
  2112.  
  2113.  
  2114. /*  Z S T I M E  --  Set or compare a file's creation date/time.  */
  2115.  
  2116. /*
  2117.  * Note: There's an additional value for parameter X on VAX/VMS systems. As
  2118.  * it's horribly painful to change a file's creation date after-the-fact we
  2119.  * call zstime with an argument  of 2 to pre-set the date when creating the
  2120.  * file. An argument of 0 (which the main-line code thinks sets the date of
  2121.  * the output file) returns success but does nothing.  Note that an invalid
  2122.  * or missing attribute packet will cause $bintim to return an error, which
  2123.  * causes the routine to exit. Since we pre-set the binary time to zero, we
  2124.  * will create the file "now", or say the incoming file is newer, whichever
  2125.  * is appropriate.
  2126.  */
  2127. int
  2128. zstime(f,yy,x) char *f; struct zattr *yy; int x; {
  2129.     int rms_sts;
  2130.     static char mth[13][4] = {    "JAN","FEB","MAR","APR",
  2131.                 "MAY","JUN","JUL","AUG",
  2132.                 "SEP","OCT","NOV","DEC",
  2133.                 ""};
  2134.     static char cdate[23];          /* Creation date yyyymmdd hh:mm:ss.00 */
  2135.     static char mnum[2];
  2136.     struct dsc$descriptor_s
  2137.     bintim_desc = {sizeof(cdate), DSC$K_DTYPE_T, DSC$K_CLASS_S,
  2138.              (char *)&cdate};
  2139.     unsigned long file_date[2], attr_date[2];
  2140.  
  2141. /* First, make a system quadword date from what we got passed */
  2142.  
  2143.     char *dptr = yy->date.val;
  2144.     if (!dptr) return(-1);
  2145.     strcpy(cdate,"dd-mmm-yyyy 00:00:00.00");
  2146.     attr_date[0]=0;                /* clear time in case of err */
  2147.     attr_date[1]=0;
  2148.     strncpy(cdate+7, dptr, 4);            /* yyyy */
  2149.     dptr += 4;
  2150.     strncpy(mnum, dptr, 2);
  2151.     strncpy(cdate+3, mth[atoi(mnum)-1], 3);    /* mm */
  2152.     dptr += 2;
  2153.     strncpy(cdate, dptr, 2);            /* dd */
  2154.     dptr += 3;
  2155.     strncpy(cdate+12, dptr, 8);            /* hhmmss */
  2156.     cdate[23] = '\0';                /* terminate */
  2157.     rms_sts = sys$bintim(&bintim_desc, &attr_date);
  2158.     if (rms_sts != SS$_NORMAL) {
  2159.     debug(F101," zstime - $bintim returns","",rms_sts);
  2160.     return(-1);
  2161.     }
  2162.     debug(F110," zstime built",cdate,0);
  2163.     sprintf(cdate, "%08X%08X", attr_date[1], attr_date[0]);
  2164.     debug(F110," $bintim attr_date", cdate, 0);
  2165.  
  2166.     if (x == 1) {
  2167.     fab_ifile = cc$rms_fab;
  2168.     fab_ifile.fab$b_fac = FAB$M_BIO | FAB$M_GET;
  2169.     fab_ifile.fab$l_fna = f;
  2170.     fab_ifile.fab$b_fns = strlen(f);
  2171.     fab_ifile.fab$l_xab = (char *)&xabdat_ifile;
  2172.     rab_ifile = cc$rms_rab;
  2173.     rab_ifile.rab$l_fab = &fab_ifile;
  2174.     xabdat_ifile = cc$rms_xabdat;
  2175.     rms_sts = sys$open(&fab_ifile);
  2176.     if (rms_sts != RMS$_NORMAL) {
  2177.         debug(F101," zstime $open failed, status","",rms_sts);
  2178.         return(-1);
  2179.     }
  2180.     memcpy(file_date, &xabdat_ifile.xab$q_cdt, 8);
  2181.     sprintf(cdate, "%08x%08x", file_date[1], file_date[0]);
  2182.     debug(F110," $bintim file_date", cdate, 0);
  2183.     rms_sts = sys$close(&fab_ifile);
  2184.     if (rms_sts != RMS$_NORMAL) {
  2185.         debug(F101," zstime $close failed, status","",rms_sts);
  2186.         return(-1);
  2187.     }
  2188.     if (attr_date[1] < file_date[1]) {
  2189.         debug(F100," zstime incoming file is older","",0);
  2190.         return(1);
  2191.     }
  2192.     if (attr_date[1] == file_date[1]) {
  2193.         if (attr_date[0] <= file_date[0]) {
  2194.         debug(F100," zstime incoming file is older, not by much","",0);
  2195.         return(1);
  2196.         }
  2197.     debug(F100," zstime incoming file is newer","",0);
  2198.     return(0);
  2199.     }
  2200.     }
  2201.  
  2202.     if (x == 0) {
  2203.     return(0);            /* say we did it (see header) */
  2204.     }
  2205.  
  2206.     if (x == 2) {
  2207.     memcpy(&xabdat_ofile.xab$q_cdt, attr_date, 8);
  2208.     return(0);            /* Set date in output file */
  2209.     }
  2210.  
  2211.     return(-1);
  2212. }
  2213.  
  2214. /*  Z K E R M I N I  --  Find initialization file.  */
  2215. /*
  2216.   Places name of init file in buffer pointed to by s.
  2217.   If no init file found, the device name of the null device is used.
  2218.   returns 0 always.
  2219. */
  2220. int
  2221. zkermini(s, s_len, def) char *s; int s_len; char *def; {
  2222.     FILE fd;
  2223.     struct dsc$descriptor_s
  2224.         dsc_in = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
  2225.         dsc_out = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0},
  2226.         dsc_def = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  2227.     int max_len;
  2228.     long unsigned int rms_s;
  2229.     unsigned long find_file_context = 0;
  2230.  
  2231.     struct TRNLIST {
  2232.     char *name;            /* ASCII file or logical name */
  2233.     unsigned char flag;        /* Zero to use default filename */
  2234.     } *p;
  2235.  
  2236.     static struct TRNLIST slist[] = {
  2237.     {"", 0},            /* Dummy first entry points to file */
  2238.     {"ckermit_ini:", 0},        /* CKERMIT_INI: points to directory */
  2239.     {"ckermit_init", 1},        /* CKERMIT_INIT points to file      */
  2240.     {"sys$login:",   0},        /* CKERMIT.INI in login directory   */
  2241.     {"", 0}
  2242.     };
  2243.     p = slist;                /* Point to list */
  2244.     if (rcflag) {            /* Name given on command line? */
  2245.     slist[0].name = def;        /* Yes, stuff its name into slist */
  2246.     slist[1].name = "";
  2247.     } else {                /* No, */
  2248.     *p++;                /* skip past dummy entry. */
  2249.     }
  2250.     while(*(p->name)) {            /* Search the list top to bottom */
  2251.  
  2252.     dsc_in.dsc$w_length = strlen(p->name); /* Length of work area */
  2253.     dsc_in.dsc$a_pointer = p->name; /* Address of string */
  2254.  
  2255.     if (!(p->flag)) {
  2256.         dsc_def.dsc$w_length = strlen(def); /* Length of work area */
  2257.         dsc_def.dsc$a_pointer = def; /* Address of string */
  2258.     } else {
  2259.         dsc_def.dsc$w_length = 0;    /* Length of work area */
  2260.         dsc_def.dsc$a_pointer = 0;    /* Address of string */
  2261.     }
  2262.     rms_s = LIB$FIND_FILE(
  2263.                 &dsc_in,    /* File spec */
  2264.                 &dsc_out,    /* Result file spec */
  2265.                 &find_file_context, /* Context */
  2266.                 &dsc_def,    /* Default file spec */
  2267.                 0,        /* Related spec */
  2268.                 0,        /* STV error */
  2269.                 0);        /* Flags */
  2270.  
  2271.     if (rms_s == RMS$_NORMAL) {
  2272.         max_len = ((unsigned short int) dsc_out.dsc$w_length < s_len ?
  2273.                (unsigned short int) dsc_out.dsc$w_length : 0);
  2274.         if (!max_len)
  2275.           fprintf(stderr,
  2276.         "%%ZKERMINI out string not long enough, ignoring .ini file\n");
  2277.         else
  2278.           strncpy(s,dsc_out.dsc$a_pointer,max_len);
  2279.         LIB$FIND_FILE_END(&find_file_context);
  2280.         LIB$SFREE1_DD(&dsc_out);    /* Return dyno memory */
  2281.         return(0);
  2282.     }
  2283.     p++;
  2284.     LIB$FIND_FILE_END(&find_file_context);
  2285.     }
  2286. /*
  2287.  * No initialization file found.  We can't return the null string because the
  2288.  * runtime library will successfully open it if the file ".;" exists in the
  2289.  * user's directory.  Instead we return the name of the null device.
  2290.  */
  2291.     strcpy(s, "NLA0:");                /* Return null init file */
  2292.     LIB$SFREE1_DD(&dsc_out);
  2293.     return(0);
  2294. }
  2295.  
  2296. static int
  2297. parse_fname(cp, cp_len, defnam, flag)
  2298. char *cp;        /* Pointer to file spec to parse */
  2299. int cp_len;        /* Length of cp field */
  2300. char *defnam;        /* Default file spec */
  2301. int flag;        /* Flag word PARSE_xxx */
  2302. {
  2303.     struct FAB fab;
  2304.     struct NAM nam;
  2305.     char expanded_name[NAM$C_MAXRSS];
  2306.     int long rms_status;
  2307.     int cur_len = 0;
  2308.  
  2309.     fab = cc$rms_fab;
  2310.     fab.fab$l_nam = &nam;
  2311.     fab.fab$l_fna = cp;
  2312.     fab.fab$b_fns = strlen(cp);
  2313.     if (defnam) {
  2314.     fab.fab$b_dns = strlen(defnam);
  2315.     fab.fab$l_dna = defnam;
  2316.     } else
  2317.     fab.fab$l_dna = 0;
  2318.  
  2319.     nam = cc$rms_nam;
  2320.     nam.nam$l_esa = (char *)&expanded_name;
  2321.     nam.nam$b_ess = sizeof(expanded_name);
  2322.  
  2323.     if (!CHECK_ERR("%%CKERMIT-W-PARSE, ",
  2324.         sys$parse(&fab)))
  2325.     return(-1);
  2326.  
  2327.     *cp = '\0';            /* Make a zero length string */
  2328.     if ((PARSE_NODE & flag) && nam.nam$b_node &&
  2329.         cur_len+nam.nam$b_node < cp_len) {
  2330.     cur_len += nam.nam$b_node;
  2331.     strncat(cp, nam.nam$l_node, (int)nam.nam$b_node);
  2332.     }
  2333.     if ((PARSE_DEVICE & flag) && nam.nam$b_dev &&
  2334.         cur_len+nam.nam$b_dev < cp_len) {
  2335.     cur_len += nam.nam$b_dev;
  2336.     strncat(cp, nam.nam$l_dev, (int)nam.nam$b_dev);
  2337.     }
  2338.     if ((PARSE_DIRECTORY & flag) && nam.nam$b_dir &&
  2339.         cur_len+nam.nam$b_dir < cp_len) {
  2340.     cur_len += nam.nam$b_dir;
  2341.     strncat(cp, nam.nam$l_dir, (int)nam.nam$b_dir);
  2342.     }
  2343.     if ((PARSE_NAME & flag) && nam.nam$b_name &&
  2344.         cur_len+nam.nam$b_name < cp_len) {
  2345.     cur_len += nam.nam$b_name;
  2346.     strncat(cp, nam.nam$l_name, (int)nam.nam$b_name);
  2347.     }
  2348.     if ((PARSE_TYPE & flag) && nam.nam$b_type &&
  2349.         cur_len+nam.nam$b_type < cp_len) {
  2350.     cur_len += nam.nam$b_type;
  2351.     strncat(cp, nam.nam$l_type, (int)nam.nam$b_type);
  2352.     }
  2353.     if ((PARSE_VERSION & flag) && nam.nam$b_ver &&
  2354.         cur_len+nam.nam$b_ver < cp_len) {
  2355.     cur_len += nam.nam$b_ver;
  2356.     strncat(cp, nam.nam$l_ver, (int)nam.nam$b_ver);
  2357.     }
  2358.     return(cur_len);
  2359. }
  2360.  
  2361. /*  Z S A T T R  --  Fill in a Kermit attribute structure for current file.  */
  2362.  
  2363. /*
  2364.  Fills in a Kermit file attribute structure for the file which is to be sent.
  2365.  Returns 0 on success with the structure filled in, or -1 on failure.
  2366.  If any string member is null, then it should be ignored.
  2367.  If any numeric member is -1, then it should be ignored.
  2368. */
  2369. int
  2370. zsattr(xx) struct zattr *xx; {
  2371.     long k;
  2372.     int x;
  2373.     static char mth[13][4] = {    "JAN","FEB","MAR","APR",
  2374.                 "MAY","JUN","JUL","AUG",
  2375.                 "SEP","OCT","NOV","DEC",
  2376.                 ""};
  2377.     static char recfm[15];        /* record format */
  2378.     static char cdate[20];          /* Creation date [yy]yymmdd[hh:mm[:ss]]*/
  2379.     static char creater_id[31];        /* Creator ID string */
  2380.     static unsigned char genprot;   /* Generic protection */
  2381.     static unsigned short lclprot;  /* Local protection */
  2382.     static long sysparam_size=0;    /* Length of system paramater buffer */
  2383.     static char *sysparam_adr=0;    /* Address of system paramater buffer */
  2384.     char type;                /* File type */
  2385.     short int asctim_retlen;
  2386.     char asctim_buf[24];        /* Work buffer for ASCTIM() */
  2387.     struct dsc$descriptor_s
  2388.      asctim_dsc = {sizeof(asctim_buf),DSC$K_DTYPE_T,DSC$K_CLASS_S,
  2389.              (char *)&asctim_buf};
  2390. /*  static long int i;  */
  2391.     static unsigned short id_len;
  2392.     static struct dsc$descriptor_s id_str =
  2393.       {31,DSC$K_DTYPE_T,DSC$K_CLASS_S,creater_id};
  2394.  
  2395. /*
  2396.  * Zero out strings
  2397.  */
  2398.  
  2399.     type = 0;
  2400.     recfm[0] = '\0';
  2401.     cdate[0] = '\0';
  2402.     creater_id[0] = '\0';
  2403.     id_len = 0;
  2404.     genprot = 0;            /* Blank protection by default */
  2405.     lclprot = 0;
  2406.  
  2407. /*
  2408.  * See if we are sending "attributes" from a REMOTE command response
  2409.  */
  2410.  
  2411.     if (*nambuf == '\0') {
  2412.     xx->lengthk = 1;        /* Number of 1K blocks rounded up */
  2413.     xx->type.len = 0;        /* File type can't be filled in here */
  2414.     xx->type.val = "";
  2415.     xx->date.len = strlen(cdate);    /* File creation date */
  2416.     xx->date.val = (char *)&cdate;
  2417.     xx->creator.len = strlen(creater_id); /* File creator */
  2418.     xx->creator.val = (char *)&creater_id;
  2419.     xx->account.len = 0;        /* File account */
  2420.     xx->account.val = "";
  2421.     xx->area.len = 0;        /* File area */
  2422.     xx->area.val = "";
  2423.     xx->password.len = 0;        /* Area password */
  2424.     xx->password.val = "";
  2425.     xx->blksize = -1L;        /* File blocksize */
  2426.     xx->access.len = 0;        /* File access */
  2427.     xx->access.val = "";
  2428.     xx->encoding.len = 1;        /* Transfer syntax */
  2429.     xx->encoding.val = "A";        /* ASCII */
  2430.     xx->disp.len = 0;        /* Disposition upon arrival */
  2431.     xx->disp.val = "";
  2432.     xx->lprotect.len = sizeof(lclprot); /* Local protection */
  2433.     xx->lprotect.val = (char *)&lclprot;
  2434.     xx->gprotect.len = sizeof(genprot); /* Generic protection */
  2435.     xx->gprotect.val = &genprot;
  2436.     xx->systemid.len = 2;        /* System ID for DEC/VMS */
  2437.     xx->systemid.val = "D7";
  2438.     xx->recfm.len = strlen(recfm);    /* Record format */
  2439.     xx->recfm.val = (char *)&recfm;
  2440.     xx->sysparam.len = sysparam_size; /* System-dependent parameters */
  2441.     xx->sysparam.val = sysparam_adr;
  2442.     xx->length = 1;            /* Length */
  2443.     return(0);            /* mumble sweet nothings at it */
  2444.     }
  2445.  
  2446. /*
  2447.  * Load the generic protection
  2448.  */
  2449.  
  2450.     x = xabpro_ifile.xab$w_pro >> XAB$V_WLD;    /* grab returned info */
  2451.     if (!(x & XAB$M_NOREAD))  genprot |= 1+32;    /* Read access */
  2452.     if (!(x & XAB$M_NOWRITE)) genprot |= 2+8;    /* Write+Append access */
  2453.     if (!(x & XAB$M_NOEXE))   genprot |= 4;    /* Execute protection */
  2454.     if (!(x & XAB$M_NODEL))   genprot |= 16;    /* Delete Access */
  2455.     lclprot = xabpro_ifile.xab$w_pro;        /* local protection */
  2456.  
  2457. /*
  2458.  * Convert creation date from an internal value to common ascii string
  2459.  */
  2460.  
  2461.     sys$asctim(&asctim_retlen,&asctim_dsc,&xabdat_ifile.xab$q_cdt,0);
  2462.     asctim_buf[asctim_retlen] = '\0';
  2463.     debug(F110," zsattr asctim_buf",asctim_buf,0);
  2464.     for (x = 0; strncmp(mth[x], asctim_buf+3,3); x++) /* Find month */
  2465.       ;
  2466.     strncpy(cdate,asctim_buf+7,4);    /* 'yyyy' */
  2467.     sprintf(cdate+4,"%02d",x+1);    /* 'mm' */
  2468.     strncpy(cdate+6,asctim_buf+0,2);    /* 'dd' */
  2469.     strncpy(cdate+8,asctim_buf+11,9);    /* ' hh:mm:ss' */
  2470.     if (cdate[6] == ' ')
  2471.     cdate[6] = '0';
  2472.     debug(F110," zsattr cdate",cdate,0);
  2473.  
  2474. /*
  2475.  * Convert the owner UIC into an alpha name
  2476.  */
  2477.  
  2478.     creater_id[0] = '\0';
  2479.     rms_sts = sys$idtoasc(xabpro_ifile.xab$l_uic,&id_len,&id_str,0,0,0);
  2480.     creater_id[id_len] = '\0';            /* terminating null, please */
  2481.     debug(F111," zsattr $idtoasc owner",creater_id,strlen(creater_id));
  2482.     if (rms_sts == SS$_NOSUCHID ||
  2483. #ifdef SS$_NORIGHTSDB    /* only vms 5 and higher */
  2484.     rms_sts == SS$_NORIGHTSDB ||
  2485. #endif /* SS$_NORIGHTSDB */
  2486.     rms_sts == SS$_IVIDENT) {
  2487.     creater_id[0] = '\0';
  2488.     rms_sts = SS$_NORMAL;            /* if unknown, null it out */
  2489.     }
  2490.     if (!(rms_sts & 1)) {
  2491.     debug(F101," zsattr $idtoasc failed, status","",rms_sts);
  2492.     return(-1);                /* fatal */
  2493.     }
  2494.  
  2495. /*
  2496.  * Fill in the record format blockette
  2497.  */
  2498.  
  2499.     if (fab_ifile.fab$b_rat & (FAB$M_CR | FAB$M_FTN | FAB$M_PRN)) {
  2500.     strcpy(recfm,"AMJ");
  2501.     } else {
  2502.     strcpy(recfm,"F");
  2503.     sprintf(recfm+1,"%05d",xabfhc_ifile.xab$w_lrl);
  2504.     }
  2505.     debug(F111," zsattr recfm",recfm,strlen(recfm));
  2506.  
  2507. /*
  2508.  * Fill in the returned data structure
  2509.  */
  2510.  
  2511.     xx->lengthk = (iflen/1024)+1;    /* Number of 1K blocks rounded up */
  2512.     xx->type.len = 0;            /* File type can't be filled in here */
  2513.     xx->type.val = "";
  2514.     xx->date.len = strlen(cdate);    /* File creation date */
  2515.     xx->date.val = (char *)&cdate;
  2516.     xx->creator.len = strlen(creater_id); /* File creator */
  2517.     xx->creator.val = (char *)&creater_id;
  2518.     xx->account.len = 0;        /* File account */
  2519.     xx->account.val = "";
  2520.     xx->area.len = 0;            /* File area */
  2521.     xx->area.val = "";
  2522.     xx->password.len = 0;        /* Area password */
  2523.     xx->password.val = "";
  2524.     xx->blksize = -1L;            /* File blocksize */
  2525.     xx->access.len = 0;            /* File access */
  2526.     xx->access.val = "";
  2527.     xx->encoding.len = 1;        /* Transfer syntax */
  2528.     xx->encoding.val = "A";        /* ASCII */
  2529.     xx->disp.len = 0;            /* Disposition upon arrival */
  2530.     xx->disp.val = "";
  2531.     xx->lprotect.len = sizeof(lclprot); /* Local protection */
  2532.     xx->lprotect.val = (char *)&lclprot;
  2533.     xx->gprotect.len = sizeof(genprot);    /* Generic protection */
  2534.     xx->gprotect.val = &genprot;
  2535.     xx->systemid.len = 2;        /* System ID for DEC/VMS */
  2536.     xx->systemid.val = "D7";
  2537.     xx->recfm.len = strlen(recfm);    /* Record format */
  2538.     xx->recfm.val = (char *)&recfm;
  2539.     xx->sysparam.len = sysparam_size;    /* System-dependent parameters */
  2540.     xx->sysparam.val = sysparam_adr;
  2541.     xx->length = iflen;            /* Length */
  2542.     debug(F111," zsattr lengthk","",xx->lengthk);
  2543.     debug(F111," zsattr length","",xx->length);
  2544.     return(0);
  2545. }
  2546.  
  2547. /* Z M K D I R  --  Create directory(s) if necessary */
  2548. /*
  2549.    Call with:
  2550.      A pointer to a file specification that might contain directory
  2551.      information.  The filename is expected to be included.
  2552.      If the file specification does not include any directory separators,
  2553.      then it is assumed to be a plain file.
  2554.      If one or more directories are included in the file specification,
  2555.      this routine tries to create them if they don't already exist.
  2556.    Returns:
  2557.      0 on success, i.e. the directory was created, or didn't need to be.
  2558.     -1 on failure to create the directory
  2559.    VMS version by Mark Berryman, Feb 94.
  2560. */
  2561.  
  2562. int
  2563. zmkdir(path) char *path; {
  2564.     unsigned int
  2565.       status,
  2566. /*    SYS$PARSE(), */
  2567.       LIB$CREATE_DIR(),
  2568.       LIB$LOCC();
  2569.  
  2570.     struct FAB dir_fab;
  2571.     struct NAM dir_nam;
  2572.     struct dsc$descriptor_s expanded_filename;
  2573.     $DESCRIPTOR(close_bracket,"]");
  2574.  
  2575.     char expanded_name[NAM$C_MAXRSS];
  2576.  
  2577.     dir_fab = cc$rms_fab;
  2578.     dir_fab.fab$l_fna = path;
  2579.     dir_fab.fab$b_fns = strlen(path);
  2580.     dir_fab.fab$l_nam = &dir_nam;
  2581.  
  2582.     dir_nam = cc$rms_nam;
  2583.     dir_nam.nam$l_esa = (char *) &expanded_name;
  2584.     dir_nam.nam$b_ess = sizeof(expanded_name);
  2585.  
  2586.     status = sys$parse(&dir_fab,0,0);
  2587.  
  2588. /*  If the result of SYS$PARSE is RMS$_NORMAL we need do nothing. */
  2589.     if (status == RMS$_NORMAL) {
  2590.     debug(F100,"zmkdir path already exists",path,0);
  2591.     return 0;
  2592.     } 
  2593.     debug(F111,"zmkdir status",path,status);
  2594.     debug(F101,"zmkdir RMS$_DNF","",RMS$_DNF);
  2595.  
  2596. /*  If the result is anything other than RMS$_DNF, it is fatal. */
  2597.     if (status != RMS$_DNF)
  2598.     return -1;
  2599.  
  2600. /*  The parse succeeded but said the directory didn't exist, so create it. */
  2601.     expanded_filename.dsc$b_class = DSC$K_CLASS_S;
  2602.     expanded_filename.dsc$b_dtype = DSC$K_DTYPE_T;
  2603.     expanded_filename.dsc$a_pointer = (char *) &expanded_name;
  2604.     expanded_filename.dsc$w_length = dir_nam.nam$b_esl;
  2605.  
  2606. /*  Strip the resulting specification to include only device and directory */
  2607.     status = LIB$LOCC(&close_bracket,&expanded_filename);
  2608.     expanded_filename.dsc$w_length = status;
  2609.  
  2610.     debug(F110,"zmkdir creating",(char *) &expanded_name,0);
  2611.     status = LIB$CREATE_DIR(&expanded_filename,0,0,0,0,0);
  2612.     debug(F101,"zmkdir final status","",status);
  2613.     return (status == SS$_CREATED ? 0 : -1);
  2614. }
  2615.  
  2616. /*  Z M A I L  --  Send file f as mail to address p.  */
  2617. /*
  2618.   Returns 0 on success
  2619.    2 if mail delivered but temp file can't be deleted
  2620.   -2 if mail can't be delivered
  2621. */
  2622. int
  2623. zmail(p,f) char *p; char *f; {
  2624.     char *zmbuf;
  2625.     static char spbuf[] = "$ mail %s %s/subj=\"Enclosed file %s\"";
  2626.     static char spbuf2[] = "%s;";
  2627.     unsigned long int sts;
  2628.  
  2629.     zmbuf = malloc(strlen(p)+(2*strlen(f))+sizeof(spbuf));
  2630.     sprintf(zmbuf,spbuf, f, p, f);
  2631.     sts = system(zmbuf);
  2632.     debug(F111," zmail: system returns status ",zmbuf,sts);
  2633.     free(zmbuf);
  2634.     if ((sts&1) != 1) {
  2635.       debug(F101," zmail: returning","",-2);
  2636.       return(-2);
  2637.     }
  2638.     zmbuf = malloc(strlen(f)+sizeof(spbuf2));
  2639.     sprintf(zmbuf,spbuf2, f);
  2640.     sts = delete(zmbuf);
  2641.     debug(F111," zmail: delete returns status ",zmbuf,sts);
  2642.     free(zmbuf);
  2643.     if (sts) sts = 2;
  2644.     debug(F101," zmail: returning","",sts);
  2645.     return(sts);
  2646. }
  2647.  
  2648. /* Z P R I N T  --  Print file f with options p.  */
  2649. /*
  2650.   Returns 0 on success, -3 on failure.
  2651. */
  2652. int
  2653. zprint(p,f) char *p; char *f; {
  2654.     char *zmbuf;
  2655.     static char spbuf[] = "$ print/delete %s %s";
  2656.     unsigned long int sts;
  2657.  
  2658.     zmbuf = malloc(strlen(p)+strlen(f)+sizeof(spbuf));
  2659.     sprintf(zmbuf,spbuf, p, f);
  2660.     sts = system(zmbuf);
  2661.     debug(F111," zprint: system returns status ",zmbuf,sts);
  2662.     free(zmbuf);
  2663.     debug(F101," zprint: returning","",(sts&1) ? 0 : -3);
  2664.     return((sts&1) ? 0 : -3);
  2665. }
  2666.  
  2667. /* Z S Y S C M D  --  Execute a DCL command with direct output.  */
  2668.  
  2669. /*
  2670.  * Since it's really difficult to have an alternate CLI under VMS (since the
  2671.  * MCR interface isn't documented and POSIX hasn't published the interface,
  2672.  * we'll just assume everybody uses DCL and hand it of to zshcmd().
  2673.  */
  2674. int
  2675. zsyscmd(s) char *s; {
  2676.     return(zshcmd(s));
  2677. }
  2678.  
  2679. /* Z S H C M D  --  Execute a default CLI command with direct output.  */
  2680.  
  2681. /*
  2682.  * As it's _REALLY_ unlikely that the user is using MCR as his default CLI,
  2683.  * and DEC doesn't document how to write any other alternate CLIs, use DCL.
  2684.  */
  2685.  
  2686. #ifndef    SS$_EXPRCLM        /* VMS doesn't return this yet, but let's */
  2687. #define SS$_EXPRCLM 10804    /* be forward-thinking and anticpate VMS */
  2688. #endif /* SS$_EXPRCLM */    /* V6.0, which will return it. */
  2689. int
  2690. zshcmd(s) char *s; {
  2691.     unsigned long sts, cc;
  2692.     int (*cct)();
  2693.     struct dsc$descriptor_s
  2694.     cmd_line = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
  2695.  
  2696.     if (check_spawn() != 0) {
  2697.     debug(F100," zshcmd: spawning prohibited by UAF flags","",0);
  2698.     return(0);
  2699.     }
  2700.     cct = signal(SIGINT,SIG_DFL);    /* Let inferior process catch ^C */
  2701.  
  2702.     cmd_line.dsc$w_length = strlen(s);
  2703.     cmd_line.dsc$a_pointer = s;
  2704.  
  2705.     if (!(*s))
  2706.     printf("Type LOGOUT to return to VMS C-Kermit.\n\n");
  2707.     sts = lib$spawn(&cmd_line, 0, 0, 0, 0, 0, &cc, 0, 0, 0, 0, 0);
  2708.     signal(SIGINT,cct);
  2709. /*
  2710.  * Note: We can't check for this beforehand as doing a getjpi for prclm will
  2711.  *     only return the UAF value, not the available value. So we try it and
  2712.  *     print this message if it didn't work.
  2713.  */
  2714.  
  2715.     if ((sts == SS$_EXQUOTA) || (sts == SS$_EXPRCLM)) {
  2716.     printf(
  2717. "Your account does not have sufficient quotas to use this command.\n");
  2718.     printf(
  2719. "Please ask your system manager to increase your UAF PRCLM quota.\n");
  2720.     }
  2721.     debug(F101,"zshcmd LIB$SPAWN sts", "", sts);
  2722.     debug(F101,"zshcmd LIB$SPAWN cc ", "", cc);
  2723.     if (sts == SS$_NORMAL)
  2724.       return(cc == SS$_NORMAL ? 1 : 0);    /* Success */
  2725.     else
  2726.       return(0);            /* Failure */
  2727. }
  2728.  
  2729. /*  Z S T R I P  --  Strip device & directory name from file specification.  */
  2730.  
  2731. /*  Strip pathname from filename "name", return pointer to result in name2 */
  2732.  
  2733. static char work[257];    /* buffer for use by zstrip and zltor */
  2734.  
  2735. VOID
  2736. zstrip(name,name2) char *name, **name2; {
  2737.     char *cp, *pp;
  2738.     debug(F110," zstrip before",name,0);
  2739.     pp = work;
  2740.  
  2741. /*  NODE::DEV:[DIR] terminates on on final ':', '>' or ']'.  */
  2742.  
  2743.     for (cp = name; *cp != '\0'; cp++) {
  2744.         if (*cp == '/' || *cp == ':' || *cp == '>' || *cp == ']') /* slash? */
  2745.       pp = work;
  2746.     else if (*cp == ';')        /* Chop off version number */
  2747.       break;
  2748.     else                /* Part of filename */
  2749.       *pp++ = *cp;
  2750.     }
  2751.     *pp = '\0';                /* Terminate the string */
  2752.     *name2 = work;
  2753.     debug(F110," zstrip after",*name2,0);
  2754. }
  2755.  
  2756. int
  2757. zchkpath(s) char *s; {
  2758. /*
  2759.   This needs to be replaced with something more intelligent.
  2760.   The idea is to see if the file, whose specification is pointed to by s,
  2761.   is in the current directory.  This function should return 0 if it s,
  2762.   nonzero otherwise.  Presently we rely on being called with a full
  2763.   filespec of the form DISK:[DEV]NAME.TYP;V, so this works more or less
  2764.   by accident.  What we really need is to call some kind of VMS service
  2765.   to get the NODE::DEV:[DIR] of the file, and compare with the current
  2766.   NODE::DEV:[DIR].
  2767. */
  2768.     char *p;
  2769.     p = zgtdir();            /* Get current dir. */
  2770.     debug(F110,"zchkpath file",s,0);
  2771.     debug(F110,"zchkpath current dir",p,0);
  2772.     return(strncmp(p,s,strlen(p)));    /* Compare it. */
  2773. }
  2774.  
  2775. #ifdef OLD_VMS
  2776. static VOID
  2777. descname(desc,name) struct dsc$descriptor_s *desc; char *name; {
  2778.     desc->dsc$w_length = strlen(name);    /* Length of name */
  2779.     desc->dsc$a_pointer = name;        /* Address */
  2780.     desc->dsc$b_class = DSC$K_CLASS_S;    /* String descriptor class */
  2781.     desc->dsc$b_dtype = DSC$K_DTYPE_T;    /* ASCII string data type */
  2782. }
  2783.  
  2784. /* VMS version of RENAME */
  2785. int /* ? */
  2786. rename(oldname, newname) char oldname[], newname[]; {
  2787.     struct dsc$descriptor_s old_desc, new_desc;
  2788.     int lib$rename_file();
  2789.  
  2790.     /* Build string descriptors */
  2791.  
  2792.     descname(&old_desc, oldname);
  2793.     descname(&new_desc, newname);
  2794.  
  2795.     /* Call lib$rename_file routine */
  2796.  
  2797.     return(lib$rename_file(&old_desc, &new_desc, 0,0,0,0,0,0,0,0,0,0));
  2798. }
  2799. #endif /* OLD_VMS */
  2800.  
  2801. /*
  2802.  * Check to see if we have spawn priv's.
  2803.  */
  2804. int
  2805. check_spawn() {
  2806.     struct itmlstdef {
  2807.     short int buflen;
  2808.     short int itmcod;
  2809.     char *bufaddr;
  2810.     long int *retlen;
  2811.     };
  2812.  
  2813.     struct itmlstdef itmlst[] =
  2814.     {4,JPI$_UAF_FLAGS,0,0,0,0,0,0};
  2815.  
  2816.     unsigned long uaf_flags, uaf_flags_size;
  2817.  
  2818.     itmlst[0].bufaddr = (char *)&uaf_flags;
  2819.     itmlst[0].retlen = &uaf_flags_size;
  2820.  
  2821.     if ((vms_status = sys$getjpiw(0, 0, 0, &itmlst, 0, 0, 0)) != SS$_NORMAL)
  2822.     return(-1);                /* Assume the worst... */
  2823.  
  2824.     if (uaf_flags & UAI$M_CAPTIVE) {
  2825.     printf("\nThis command cannot be executed. Your account is CAPTIVE.\n\n");
  2826.     return(-1);
  2827.     }
  2828. #ifdef    UAI$M_RESTRICTED            /* for pre-V5.2 systems */
  2829.     if (uaf_flags & UAI$M_RESTRICTED) {
  2830.     printf("\nThis command cannot be executed. Your account is CAPTIVE.\n\n");
  2831.     return(-1);
  2832.     }
  2833. #endif    /* uai$v_restricted */
  2834.     return(0);
  2835. }
  2836.  
  2837. /*
  2838.  * Stuff having to do with SET FILE TYPE LABELED
  2839.  */
  2840. char *
  2841. get_vms_vers() {
  2842.     static char sysver[9];
  2843.     int len;
  2844.     struct itmlst {
  2845.           short int buflen;
  2846.           short int code;
  2847.           char *bufadr;
  2848.           int *retlen;
  2849.           } vms_sysver[2];
  2850.  
  2851.     vms_sysver[0].buflen = 8;
  2852.     vms_sysver[0].code = SYI$_VERSION;
  2853.     vms_sysver[0].bufadr = (char *)&sysver;
  2854.     vms_sysver[0].retlen = &len;
  2855.     vms_sysver[1].buflen = 0;
  2856.     vms_sysver[1].code = 0;
  2857.     sys$getsyiw(0,0,0,&vms_sysver,0,0,0);
  2858.     sysver[8]='\0';
  2859.     len = 7;
  2860.     while (sysver[len] == ' ') {
  2861.     sysver[len] = '\0';
  2862.     len--;
  2863.     }
  2864.     return(sysver);
  2865. }
  2866.  
  2867. int
  2868. do_label_send(name) char *name; {
  2869.     int pad_size;
  2870.  
  2871.     zinptr += sprintf(zinptr,"KERMIT LABELED FILE:02D704VERS");
  2872.     zinptr += sprintf(zinptr,"%08d%s", strlen(get_vms_vers()), get_vms_vers());
  2873.     zinptr += sprintf(zinptr,"05KVERS00000008%08ld", vernum);
  2874.     zinptr += sprintf(zinptr,"07VMSNAME%08d", strlen(name));
  2875.     zinptr += sprintf(zinptr,"%s", name);
  2876.     zinptr += sprintf(zinptr,"07VMSFILE%08d", 70);
  2877.     memmove(zinptr, &xabpro_ifile.xab$w_pro, 2);
  2878.     zinptr += 2;
  2879.     memmove(zinptr, &xabpro_ifile.xab$l_uic, 4);
  2880.     zinptr += 4;
  2881.     memmove(zinptr, &fab_ifile.fab$b_rfm, 1);
  2882.     zinptr += 1;
  2883.     memmove(zinptr, &fab_ifile.fab$b_org, 1);
  2884.     zinptr += 1;
  2885.     memmove(zinptr, &fab_ifile.fab$b_rat, 1);
  2886.     zinptr += 1;
  2887.     memmove(zinptr, &uchar, 4);            /* Dummy for file chars. */
  2888.     zinptr += 4;
  2889.     memmove(zinptr, &fab_ifile.fab$b_fsz, 1);
  2890.     zinptr += 1;
  2891.     memmove(zinptr, &xabfhc_ifile.xab$w_lrl, 2);
  2892.     zinptr += 2;
  2893.     memmove(zinptr, &fab_ifile.fab$w_mrs, 2);
  2894.     zinptr += 2;
  2895.     memmove(zinptr, &xabfhc_ifile.xab$l_ebk, 4);
  2896.     zinptr += 4;
  2897.     memmove(zinptr, &xabfhc_ifile.xab$w_ffb, 2);
  2898.     zinptr += 2;
  2899.     memmove(zinptr, &xabfhc_ifile.xab$l_hbk, 4);
  2900.     zinptr += 4;
  2901.     memmove(zinptr, &fab_ifile.fab$w_deq, 2);
  2902.     zinptr += 2;
  2903.     memmove(zinptr, &fab_ifile.fab$b_bks, 1);
  2904.     zinptr += 1;
  2905.     memmove(zinptr, &fab_ifile.fab$w_gbc, 2);
  2906.     zinptr += 2;
  2907.     memmove(zinptr, &xabfhc_ifile.xab$w_verlimit, 2);
  2908.     zinptr += 2;
  2909.     memmove(zinptr, &fab_ifile.fab$b_rfm+1, 1);    /* This is fab$b_journal */
  2910.     zinptr += 1;
  2911.     memmove(zinptr, &xabdat_ifile.xab$q_cdt, 8);
  2912.     zinptr += 8;
  2913.     memmove(zinptr, &xabdat_ifile.xab$q_rdt, 8);
  2914.     zinptr += 8;
  2915.     memmove(zinptr, &xabdat_ifile.xab$w_rvn, 2);
  2916.     zinptr += 2;
  2917.     memmove(zinptr, &xabdat_ifile.xab$q_edt, 8);
  2918.     zinptr += 8;
  2919.     memmove(zinptr, &xabdat_ifile.xab$q_bdt, 8);
  2920.     zinptr += 8;
  2921.     if (xabpro_ifile.xab$w_acllen != 0) {
  2922.     zinptr += sprintf(zinptr,"06VMSACL%08d", xabpro_ifile.xab$w_acllen);
  2923.     memmove(zinptr, &aclbuf, xabpro_ifile.xab$w_acllen);
  2924.     zinptr += xabpro_ifile.xab$w_acllen;
  2925.     }
  2926.     zinptr += sprintf(zinptr,"04DATA00000000");
  2927.     zincnt = (zinptr - zinbuffer);        /* Size of this beast */
  2928.     zinptr = zinbuffer;                /* Reset pointer for readout */
  2929.     return(1);
  2930. }
  2931.  
  2932. /*
  2933.  * Note that we don't honor SET FILE COLLISION APPEND for labeled receives -
  2934.  * the whole point of labeled receives is to generate an exact copy of the
  2935.  * source file, attributes and all.
  2936.  */
  2937. int
  2938. do_label_recv() {
  2939.     char *recv_ptr;
  2940.     char buffer[16];
  2941.     char vmsfile[70];
  2942.     char *filptr = vmsfile;
  2943.     int lblen, alen;
  2944.     int gotname = 0, gotfile = 0, gotacl = 0;
  2945.     char *i, *j;
  2946.     unsigned short jnlflg;
  2947.  
  2948.     debug(F101," in do_label_recv, options","",ofile_lblopts);
  2949.     ofile_lblproc = 1;                /* Don't come here again */
  2950.  
  2951.     if (strncmp(zoutbuffer,"KERMIT LABELED FILE:02D704VERS",30) != 0)
  2952.     return(0);            /* Just continue if unlabeled */
  2953.  
  2954.     recv_ptr = zoutbuffer+30;        /* start at front of buffer */
  2955.  
  2956.     memcpy(buffer, recv_ptr, 8);
  2957.     recv_ptr += 8;
  2958.     buffer[8] = '\0';
  2959.     lblen = atoi(buffer);
  2960.  
  2961.     memcpy(buffer, recv_ptr, lblen);
  2962.     recv_ptr += lblen;
  2963.     buffer[lblen] = '\0';
  2964.     debug(F110,"  file created under VAX/VMS: ",buffer,0);
  2965.  
  2966.     memcpy(buffer, recv_ptr, 7);
  2967.     recv_ptr += 7;
  2968.     if (strncmp(buffer, "05KVERS", 7) != 0) {
  2969.     debug(F100,"  lost sync at KVERS","",0);
  2970.     return(-1);
  2971.     }
  2972.  
  2973.     memcpy(buffer, recv_ptr, 8);
  2974.     recv_ptr += 8;
  2975.     buffer[8] = '\0';
  2976.     lblen = atoi(buffer);
  2977.  
  2978.     memcpy(buffer, recv_ptr, lblen);
  2979.     recv_ptr += lblen;
  2980.     buffer[lblen] = '\0';
  2981.     debug(F110,"  file created with C-Kermit/VMS: ",buffer,0);
  2982.  
  2983.     next_label:
  2984.     memcpy(buffer, recv_ptr, 2);
  2985.     recv_ptr += 2;
  2986.     buffer[2] = '\0';
  2987.     lblen = atoi(buffer);
  2988.     if (lblen == 0) {
  2989.     debug(F100,"  lost sync at next_label: ","",0);
  2990.     return(-1);
  2991.     }
  2992.  
  2993.     memcpy(buffer, recv_ptr, lblen);
  2994.     recv_ptr += lblen;
  2995.     buffer[lblen] = '\0';
  2996.     debug(F110,"  found tag: ",buffer,0);
  2997.     if (strcmp(buffer, "VMSNAME") == 0) {
  2998.     memcpy(buffer, recv_ptr, 8);
  2999.         recv_ptr += 8;
  3000.     buffer[8] = '\0';
  3001.     lblen = atoi(buffer);
  3002.     memcpy(ofile_vmsname, recv_ptr, lblen);
  3003.         recv_ptr += lblen;
  3004.     ofile_vmsname[lblen] = '\0';
  3005.     gotname++;
  3006.     debug(F110,"  loaded file name block as: ",ofile_vmsname,0);
  3007.     i = strstr(ofile_vmsname, "::");
  3008.     if (i != NULL) {
  3009.         i += 2;
  3010.         memmove(ofile_vmsname, i, strlen(ofile_vmsname));
  3011.     }
  3012.         if ((ofile_lblopts & LBL_PTH) == 0) {
  3013.         i = strrchr(ofile_vmsname, ':');
  3014.         j = strrchr(ofile_vmsname, ']');
  3015.         if (j == NULL)
  3016.         j = strrchr(ofile_vmsname, '>');
  3017.         if (j > i)
  3018.         i = j;
  3019.         i++;
  3020.         memmove(ofile_vmsname, i, strlen(ofile_vmsname));
  3021.     }
  3022.     if (strchr(ofile_vmsname, ';') != NULL) {
  3023.         for (alen = strlen(ofile_vmsname);
  3024.          ofile_vmsname[alen] != ';';
  3025.          alen--)
  3026.         ;
  3027.         ofile_vmsname[alen] = '\0';
  3028.     }
  3029.     debug(F110,"  resultant filespec: ",ofile_vmsname,0);
  3030.     goto next_label;
  3031.     }
  3032.     else if (strcmp(buffer, "VMSFILE") == 0) {
  3033.     memcpy(buffer, recv_ptr, 8);
  3034.     recv_ptr += 8;
  3035.     buffer[8] = '\0';
  3036.     lblen = atoi(buffer);
  3037.     memcpy(vmsfile, recv_ptr, lblen);
  3038.     recv_ptr += lblen;
  3039.     vmsfile[lblen] = '\0';
  3040.     gotfile++;
  3041.     debug(F100,"  loaded file attribute block","",0);
  3042.     goto next_label;
  3043.     }
  3044.     else if (strcmp(buffer, "VMSACL") == 0) {
  3045.     memcpy(buffer, recv_ptr, 8);
  3046.     recv_ptr += 8;
  3047.     buffer[8] = '\0';
  3048.     ofile_acllen = atoi(buffer);
  3049.     memcpy(ofile_vmsacl, recv_ptr, ofile_acllen);
  3050.     recv_ptr += ofile_acllen;
  3051.     ofile_vmsacl[ofile_acllen] = '\0';
  3052.     gotacl++;
  3053.     debug(F100,"  loaded file ACL block","",0);
  3054.     goto next_label;
  3055.     }
  3056.     else if (strcmp(buffer, "DATA") == 0) {
  3057.     memcpy(buffer, recv_ptr, 8);
  3058.     recv_ptr += 8;
  3059.     buffer[8] = '\0';
  3060.     lblen = atoi(buffer);
  3061.     if (lblen != 0) {
  3062.         debug(F101,"  length of DATA tag not zero","",lblen);
  3063.         return(-1);
  3064.     }
  3065.     debug(F100,"  positioned at start of file data","",0);
  3066.     goto all_set;
  3067.     }
  3068.     else {
  3069.     debug(F110,"  unrecognized label: ",buffer,0);
  3070.     memcpy(buffer, recv_ptr, 8);
  3071.     recv_ptr += 8;
  3072.     buffer[8] = '\0';
  3073.     lblen = atoi(buffer);
  3074.     recv_ptr += lblen;
  3075.     goto next_label;
  3076.     }
  3077.  
  3078.     all_set:
  3079.     if (gotfile != 1 || gotname != 1) {
  3080.     debug(F100,"  missing one or more required labels","",0);
  3081.     return(-1);
  3082.     }
  3083.  
  3084. /*
  3085.  * Prep the characteristics
  3086.  */
  3087.  
  3088.     fab_ofile.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
  3089.     fab_ofile.fab$l_fop = FAB$M_MXV;
  3090.     if ((ofile_lblopts & LBL_NAM) != 0) {
  3091.     fab_ofile.fab$l_fna = ofile_vmsname;
  3092.     fab_ofile.fab$b_fns = strlen(ofile_vmsname);
  3093.     }
  3094.     fab_ofile.fab$l_xab = (char *)&xabdat_ofile;
  3095.     rab_ofile = cc$rms_rab;
  3096.     rab_ofile.rab$l_fab = &fab_ofile;
  3097.     xabdat_ofile = cc$rms_xabdat;
  3098.     xabdat_ofile.xab$l_nxt = (char *)&xabrdt_ofile;
  3099.     xabrdt_ofile = cc$rms_xabrdt;
  3100.     xabrdt_ofile.xab$l_nxt = (char *)&xabfhc_ofile;
  3101.     xabfhc_ofile = cc$rms_xabfhc;
  3102.     xabfhc_ofile.xab$l_nxt = (char *)&xabpro_ofile;
  3103.     xabpro_ofile = cc$rms_xabpro;
  3104.     xabpro_ofile.xab$l_nxt = (char *)&xaball_ofile;
  3105.     xaball_ofile = cc$rms_xaball;
  3106.  
  3107. /*
  3108.  * Load 'em up
  3109.  */
  3110.  
  3111.     memmove(&xabpro_ofile.xab$w_pro, filptr, 2);
  3112.     filptr += 2;
  3113.     if ((ofile_lblopts & LBL_OWN) != 0)
  3114.     memmove(&xabpro_ofile.xab$l_uic, filptr, 4);
  3115.     filptr += 4;
  3116.     memmove(&fab_ofile.fab$b_rfm, filptr, 1);
  3117.     filptr += 1;
  3118.     memmove(&fab_ofile.fab$b_org, filptr, 1);
  3119.     filptr += 1;
  3120.     memmove(&fab_ofile.fab$b_rat, filptr, 1);
  3121.     filptr += 1;
  3122.     filptr += 4;            /* reserved */
  3123.     memmove(&fab_ofile.fab$b_fsz, filptr, 1);
  3124.     filptr += 1;
  3125.     memmove(&xabfhc_ofile.xab$w_lrl, filptr, 2);
  3126.     filptr += 2;
  3127.     memmove(&fab_ofile.fab$w_mrs, filptr, 2);
  3128.     filptr += 2;
  3129.     memmove(&xabfhc_ofile.xab$l_ebk, filptr, 4);
  3130.     filptr += 4;
  3131. /* preserve this as RMS won't remember it for us */
  3132.     memmove(&ofile_ffb, filptr, 2);
  3133.     filptr += 2;
  3134.     memmove(&xaball_ofile.xab$l_alq, filptr, 4);
  3135.     filptr += 4;
  3136.     memmove(&xaball_ofile.xab$w_deq, filptr, 2);
  3137.     filptr += 2;
  3138.     memmove(&xaball_ofile.xab$b_bkz, filptr, 1);
  3139.     filptr += 1;
  3140.     memmove(&fab_ofile.fab$w_gbc, filptr, 2);
  3141.     filptr += 2;
  3142.     memmove(&xabfhc_ofile.xab$w_verlimit, filptr, 2);
  3143.     filptr += 2;
  3144.     memmove(&jnlflg, filptr, 1);
  3145.     if (jnlflg !=0)
  3146.     debug(F100,"  journaling status removed for file","",0);
  3147.     filptr += 1;
  3148.     memmove(&xabdat_ofile.xab$q_cdt, filptr, 8);
  3149.     filptr += 8;
  3150.     memmove(&revdat, filptr, 8);
  3151.     filptr += 8;
  3152.     memmove(&revnum, filptr, 2);
  3153.     filptr += 2;
  3154.     memmove(&xabdat_ofile.xab$q_edt, filptr, 8);
  3155.     filptr += 8;
  3156.     if ((ofile_lblopts & LBL_BCK) != 0)
  3157.     memmove(&xabdat_ofile.xab$q_bdt, filptr, 8);
  3158.     filptr += 8;
  3159.  
  3160. /*
  3161.  * ACL's?
  3162.  */
  3163.  
  3164.     if ((ofile_lblopts & LBL_ACL) != 0 && gotacl != 0) {
  3165.     xabpro_ofile.xab$l_aclbuf = (char *)&ofile_vmsacl;
  3166.     xabpro_ofile.xab$w_aclsiz = ofile_acllen;
  3167.     }
  3168.  
  3169. /*
  3170.  * Give it a quick whirl around the dance floor
  3171.  */
  3172.  
  3173.     rms_sts = sys$create(&fab_ofile);
  3174.     if (!(rms_sts & 1)) {
  3175.     debug(F101,"  $create failed, status","",rms_sts);
  3176.     return(-1);
  3177.     }
  3178.  
  3179.     if((ofile_lblopts & LBL_ACL) != 0 && gotacl != 0) {
  3180.     if (!(xabpro_ofile.xab$l_aclsts & 1)) {
  3181.         debug(F101,"  ACL chain failed, status","",xabpro_ofile.xab$l_aclsts);
  3182.         return(-1);
  3183.     }
  3184.     }
  3185.  
  3186.     rms_sts = sys$connect(&rab_ofile);
  3187.     if (!(rms_sts & 1)) {
  3188.     debug(F101,"  $connect failed, status","",rms_sts);
  3189.     return(-1);
  3190.     }
  3191.  
  3192. /*
  3193.  * Slide the remainder of the data to the head of the buffer and adjust the
  3194.  * counter and pointer. This will cause the buffer to be re-filled to the full
  3195.  * 32Kb capacity, which is necessary for proper operation of zoutdump().
  3196.  */
  3197.  
  3198.     zoutcnt -= ((char *)recv_ptr - (char *)zoutbuffer);
  3199.     memcpy(zoutbuffer, recv_ptr, zoutcnt);
  3200.     zoutptr = zoutbuffer + zoutcnt;
  3201.     return(1);                /* Go fill some more */
  3202. }
  3203.  
  3204.  
  3205.