ftp.nice.ch/pub/next/unix/archiver/unzip.5.01.N.s.tar.gz#/unzip/VMS/vms.c

This is vms.c in view mode; [Download] [Up]

/*************************************************************************
 *                                                                       *
 * Copyright (C) 1992 Igor Mandrichenko.                                 *
 * Permission is granted to any individual or institution to use, copy,  *
 * or redistribute this software so long as all of the original files    *
 * are included unmodified, that it is not sold for profit, and that     *
 * this copyright notice is retained.                                    *
 *                                                                       *
 *************************************************************************/

/*
 *    vms.c  by Igor Mandrichenko
 *    version 1.2-1
 *
 *    This module contains routines to extract VMS file attributes
 *    from extra field and create file with these attributes.  This
 *    source is mainly based on sources of file_io.c from UNZIP 4.1
 *    by Info-ZIP.  [Info-ZIP note:  very little of this code is from
 *    file_io.c; it has virtually been written from the ground up.
 *    Of the few lines which are from the older code, most are mine
 *    (G. Roelofs) and I make no claims upon them.  On the contrary,
 *    my/our thanks to Igor for his contributions!]
 */

/*
 *      Revision history:
 *      1.0-1   Mandrichenko    16-feb-1992
 *              Recognize -c option
 *      1.0-2   Mandrichenko    17-feb-1992
 *              Do not use ASYnchroneous mode.
 *      1.0-3   Mandrichenko    2-mar-1992
 *              Make code more standard
 *              Use lrec instead of crec -- unzip4.2p does not provide
 *              crec now.
 *      1.1     Mandrichenko    5-mar-1992
 *              Make use of asynchronous output.
 *              Be ready to extract RMS blocks of invalid size (because diff
 *              VMS version used to compress).
 *      1.1-1   Mandrichenko    11-mar-1992
 *              Use internal file attributes saved in pInfo to decide
 *              if the file is text.  [GRR:  temporarily disabled, since
 *              no way to override and force binary extraction]
 *      1.1-2   Mandrichenko    13-mar-1992
 *              Do not restore owner/protection info if -X not specified.
 *      1.1-3   Mandrichenko    30-may-1992
 *              Set revision date/time to creation date/time if none specified
 *              Take quiet flag into account.
 *      1.1-4   Cave Newt       14-jun-1992
 *              Check zipfile for variable-length format (unzip and zipinfo).
 *	1.2	Mandrichenko	21-jun-1992
 *		Use deflation/inflation for compression of extra blocks
 *		Free all allocated space
 *	1.2-1	Mandrichenko	23-jun-1992
 *		Interactively select an action when file exists
 */

#ifdef VMS			/*      VMS only !      */

#ifndef SYI$_VERSION
#define SYI$_VERSION 4096	/* VMS 5.4 definition */
#endif

#ifndef VAXC
				/* This definition may be missed */
struct XAB {
    unsigned char xab$b_cod;
    unsigned char xab$b_bln;
    short int xabdef$$_fill_1;
    char *xab$l_nxt;
};

#endif

#include "unzip.h"
#include <ctype.h>
#include <descrip.h>
#include <syidef.h>

#define ERR(s) !((s) & 1)

#define BUFS512 8192*2		/* Must be a multiple of 512 */

/*
*   Local static storage
*/
static struct FAB fileblk;
static struct XABDAT dattim;
static struct XABRDT rdt;
static struct RAB rab;

static struct FAB *outfab = 0;
static struct RAB *outrab = 0;
static struct XABFHC *xabfhc = 0;
static struct XABDAT *xabdat = 0;
static struct XABRDT *xabrdt = 0;
static struct XABPRO *xabpro = 0;
static struct XABKEY *xabkey = 0;
static struct XABALL *xaball = 0;
struct XAB *first_xab = 0L, *last_xab = 0L;

static char query = 0;
static int text_file = 0;

static char locbuf[BUFS512];
static int loccnt = 0;
static char *locptr;

static int WriteBuffer();
static int _flush_blocks();
static int _flush_records();
static byte *extract_block();
static void message();
static int get_vms_version();
static void free_up();
static int replace();

struct bufdsc
{
    struct bufdsc *next;
    byte *buf;
    int bufcnt;
};

static struct bufdsc b1, b2, *curbuf;
static byte buf1[BUFS512], buf2[BUFS512];


int check_format()		/* return non-0 if format is variable-length */
{
    int rtype;
    struct FAB fab;

    fab = cc$rms_fab;
    fab.fab$l_fna = zipfn;
    fab.fab$b_fns = strlen(zipfn);
    sys$open(&fab);
    rtype = fab.fab$b_rfm;
    sys$close(&fab);

    if (rtype == FAB$C_VAR || rtype == FAB$C_VFC)
    {
	fprintf(stderr,
		"\n     Error:  zipfile is in variable-length record format.  Please\n\
     run \"bilf l %s\" to convert the zipfile to stream-LF\n\
     record format.  (Bilf.exe, bilf.c and make_bilf.com are included\n\
     in the VMS UnZip source distribution.)\n\n", zipfn);
	return 2;		/* 2:  error in zipfile */
    }

    return 0;
}


#ifndef ZIPINFO

int create_output_file()
{				/* return non-0 if sys$create failed */
    int ierr, yr, mo, dy, hh, mm, ss;
    char timbuf[24];		/* length = first entry in "stupid" + 1 */
    int attr_given = 0;		/* =1 if VMS attributes are present in
				*     extra_field */

    rab = cc$rms_rab;		/* fill FAB & RAB with default values */
    fileblk = cc$rms_fab;

    text_file =/* pInfo->text || */aflag || cflag;

    if (attr_given = find_vms_attrs())
    {
	text_file = 0;
	if (cflag)
	{
	    printf("Cannot put VMS file %s to stdout.\n",
		   filename);
	    free_up();
	    return 50;
	}
    }

    if (!attr_given)
    {
	outfab = &fileblk;
	outfab->fab$l_xab = 0L;
	if (text_file)
	{
	    outfab->fab$b_rfm = FAB$C_VAR;	/* variable length records */
	    outfab->fab$b_rat = FAB$M_CR;	/* carriage-return carriage ctrl */
	}
	else
	{
	    outfab->fab$b_rfm = FAB$C_STMLF;	/* stream-LF record format */
	    outfab->fab$b_rat = FAB$M_CR;	/* carriage-return carriage ctrl */
	}
    }

    if (!cflag)
	outfab->fab$l_fna = filename;
    else
	outfab->fab$l_fna = "sys$output:";

    outfab->fab$b_fns = strlen(outfab->fab$l_fna);

    if ((!attr_given) || xabdat == 0 || xabrdt == 0)	/* Use date/time info
							 *  from zipfile if
							 *  no attributes given
							 */
    {
	static char *month[] =
	    {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
	     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};

	/*  fixed-length string descriptor: */
	struct dsc$descriptor stupid =
	    {23, DSC$K_DTYPE_T, DSC$K_CLASS_S, timbuf};

	yr = ((lrec.last_mod_file_date >> 9) & 0x7f) + 1980;
	mo = ((lrec.last_mod_file_date >> 5) & 0x0f) - 1;
	dy = (lrec.last_mod_file_date & 0x1f);
	hh = (lrec.last_mod_file_time >> 11) & 0x1f;
	mm = (lrec.last_mod_file_time >> 5) & 0x3f;
	ss = (lrec.last_mod_file_time & 0x1f) * 2;

	dattim = cc$rms_xabdat;	/* fill XABs with default values */
	rdt = cc$rms_xabrdt;
	sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00", dy, month[mo], yr,
		hh, mm, ss);
	sys$bintim(&stupid, &dattim.xab$q_cdt);
	memcpy(&rdt.xab$q_rdt, &dattim.xab$q_cdt, sizeof(rdt.xab$q_rdt));

	if ((!attr_given) || xabdat == 0L)
	{
	    dattim.xab$l_nxt = outfab->fab$l_xab;
	    outfab->fab$l_xab = &dattim;
	}
    }

    outfab->fab$w_ifi = 0;	/* Clear IFI. It may be nonzero after ZIP */

    ierr = sys$create(outfab);
    if (ierr == RMS$_FEX)
	ierr = replace();

    if (ierr == 0)		/* Canceled */
	return free_up(), 1;

    if (ERR(ierr))
    {
	char buf[256];

	sprintf(buf, "[ Cannot create output file %s ]\n", filename);
	message(buf, ierr);
	message("", outfab->fab$l_stv);
	free_up();
	return (1);
    }

    if (!text_file && !cflag)	/* Do not reopen text files and stdout
				*  Just open them in right mode         */
    {
	/*
	*       Reopen file for Block I/O with no XABs.
	*/
	if ((ierr = sys$close(outfab)) != RMS$_NORMAL)
	{
#ifdef DEBUG
	    message("[ create_output_file: sys$close failed ]\n", ierr);
	    message("", outfab->fab$l_stv);
#endif
	    fprintf(stderr, "Can't create output file:  %s\n", filename);
	    free_up();
	    return (1);
	}


	outfab->fab$b_fac = FAB$M_BIO | FAB$M_PUT;	/* Get ready for block
							 * output */
	outfab->fab$l_xab = 0L;	/* Unlink all XABs */

	if ((ierr = sys$open(outfab)) != RMS$_NORMAL)
	{
	    char buf[256];

	    sprintf(buf, "[ Cannot open output file %s ]\n", filename);
	    message(buf, ierr);
	    message("", outfab->fab$l_stv);
	    free_up();
	    return (1);
	}
    }

    outrab = &rab;
    rab.rab$l_fab = outfab;
    if (!text_file) rab.rab$l_rop |= RAB$M_BIO;
    if (!text_file) rab.rab$l_rop |= RAB$M_ASY;
    rab.rab$b_rac = RAB$C_SEQ;

    if ((ierr = sys$connect(outrab)) != RMS$_NORMAL)
    {
#ifdef DEBUG
	message("create_output_file: sys$connect failed.\n", ierr);
	message("", outfab->fab$l_stv);
#endif
	fprintf(stderr, "Can't create output file:  %s\n", filename);
	free_up();
	return (1);
    }

    locptr = &locbuf[0];
    loccnt = 0;

    b1.buf = &buf1[0];
    b1.bufcnt = 0;
    b1.next = &b2;
    b2.buf = &buf2[0];
    b2.bufcnt = 0;
    b2.next = &b1;
    curbuf = &b1;

    return (0);
}

static int replace()
{				/*
				*	File exists. Inquire user about futher action.
				*/
    char answ[10];
    struct NAM nam;
    int ierr;

    if (query == 0)
    {
	do
	{
	    fprintf(stderr,
		    "Replace %s : [o]verwrite, new [v]ersion or [c]ancel (O,V,C - all) ? ",
		    filename);
	    fflush(stderr);
	} while (fgets(answ, 9, stderr) == NULL && !isalpha(answ[0])
		 && tolower(answ[0]) != 'o'
		 && tolower(answ[0]) != 'v'
		 && tolower(answ[0]) != 'c');

	if (isupper(answ[0]))
	    query = answ[0] = tolower(answ[0]);
    }
    else
	answ[0] = query;

    switch (answ[0])
    {
	case 'c':
	    ierr = 0;
	    break;
	case 'v':
	    nam = cc$rms_nam;
	    nam.nam$l_rsa = filename;
	    nam.nam$b_rss = FILNAMSIZ - 1;

	    outfab->fab$l_fop |= FAB$M_MXV;
	    outfab->fab$l_nam = &nam;

	    ierr = sys$create(outfab);
	    if (!ERR(ierr))
	    {
		outfab->fab$l_nam = 0L;
		filename[outfab->fab$b_fns = nam.nam$b_rsl] = 0;
	    }
	    break;
	case 'o':
	    outfab->fab$l_fop |= FAB$M_SUP;
	    ierr = sys$create(outfab);
	    break;
    }
    return ierr;
}

/*
*   Extra record format
*   ===================
*   signature       (2 bytes)   = 'I','M'
*   size            (2 bytes)
*   block signature (4 bytes)
*   flags           (2 bytes)
*   uncomprssed size(2 bytes)
*   reserved        (4 bytes)
*   data            ((size-12) bytes)
*   ....
*/

#define BC_MASK 	07	/* 3 bits for compression type */
#define BC_STORED	0	/* Stored */
#define BC_00		1	/* 0byte -> 0bit compression */
#define BC_DEFL		2	/* Deflated */

struct extra_block
{
    UWORD sig;			/* Extra field block header structure */
    UWORD size;
    ULONG bid;
    UWORD flags;
    UWORD length;
    ULONG reserved;
    byte body[1];
};

/*
 *   Extra field signature and block signatures
 */

#define SIGNATURE "IM"
#define FABL    (cc$rms_fab.fab$b_bln)
#define RABL    (cc$rms_rab.rab$b_bln)
#define XALLL   (cc$rms_xaball.xab$b_bln)
#define XDATL   (cc$rms_xabdat.xab$b_bln)
#define XFHCL   (cc$rms_xabfhc.xab$b_bln)
#define XKEYL   (cc$rms_xabkey.xab$b_bln)
#define XPROL   (cc$rms_xabpro.xab$b_bln)
#define XRDTL   (cc$rms_xabrdt.xab$b_bln)
#define XSUML   (cc$rms_xabsum.xab$b_bln)
#define EXTBSL  4		/* Block signature length   */
#define RESL    8		/* Rserved 8 bytes  */
#define EXTHL   (4+EXTBSL)
#define FABSIG  "VFAB"
#define XALLSIG "VALL"
#define XFHCSIG "VFHC"
#define XDATSIG "VDAT"
#define XRDTSIG "VRDT"
#define XPROSIG "VPRO"
#define XKEYSIG "VKEY"
#define XNAMSIG "VNAM"
#define VERSIG  "VMSV"



#define W(p)    (*(unsigned short*)(p))
#define L(p)    (*(unsigned long*)(p))
#define EQL_L(a,b)      ( L(a) == L(b) )
#define EQL_W(a,b)      ( W(a) == W(b) )

/****************************************************************
 * Function find_vms_attrs scans ZIP entry extra field if any   *
 * and looks for VMS attribute records. Returns 0 if either no  *
 * attributes found or no fab given.                            *
 ****************************************************************/
int find_vms_attrs()
{
    byte *scan = extra_field;
    struct extra_block *blk;
    int len;

    outfab = xabfhc = xabdat = xabrdt = xabpro = first_xab = last_xab = 0L;

    if (scan == NULL)
	return 0;
    len = lrec.extra_field_length;

#define LINK(p) {	/* Link xaballs and xabkeys into chain */	\
                if( first_xab == 0L )                   \
                        first_xab = p;                  \
                if( last_xab != 0L )                    \
                        last_xab -> xab$l_nxt = p;      \
                last_xab = p;                           \
                p -> xab$l_nxt = 0;                     \
        }
    /* End of macro LINK */

    while (len > 0)
    {
	blk = (struct block *) scan;
	if (EQL_W(&blk->sig, SIGNATURE))
	{
	    byte *block_id;

	    block_id = &blk->bid;
	    if (EQL_L(block_id, FABSIG))
	    {
		outfab = (struct FAB *) extract_block(blk, 0,
						      &cc$rms_fab, FABL);
	    }
	    else if (EQL_L(block_id, XALLSIG))
	    {
		xaball = (struct XABALL *) extract_block(blk, 0,
							 &cc$rms_xaball, XALLL);
		LINK(xaball);
	    }
	    else if (EQL_L(block_id, XKEYSIG))
	    {
		xabkey = (struct XABKEY *) extract_block(blk, 0,
							 &cc$rms_xabkey, XKEYL);
		LINK(xabkey);
	    }
	    else if (EQL_L(block_id, XFHCSIG))
	    {
		xabfhc = (struct XABFHC *) extract_block(blk, 0,
							 &cc$rms_xabfhc, XFHCL);
	    }
	    else if (EQL_L(block_id, XDATSIG))
	    {
		xabdat = (struct XABDAT *) extract_block(blk, 0,
							 &cc$rms_xabdat, XDATL);
	    }
	    else if (EQL_L(block_id, XRDTSIG))
	    {
		xabrdt = (struct XABRDT *) extract_block(blk, 0,
							 &cc$rms_xabrdt, XRDTL);
	    }
	    else if (EQL_L(block_id, XPROSIG))
	    {
		xabpro = (struct XABPRO *) extract_block(blk, 0,
							 &cc$rms_xabpro, XPROL);
	    }
	    else if (EQL_L(block_id, VERSIG))
	    {
		char verbuf[80];
		int verlen = 0;
		byte *vers;
		char *m;

		get_vms_version(verbuf, 80);
		vers = extract_block(blk, &verlen, 0, 0);
		if ((m = strrchr(vers, '-')) != NULL)
		    *m = 0;	/* Cut out release number */
		if (strcmp(verbuf, vers) && quietflg == 0)
		{
		    printf("[ Warning: VMS version mismatch.");

		    printf("   This version %s --", verbuf);
		    strncpy(verbuf, vers, verlen);
		    verbuf[verlen] = 0;
		    printf(" version made by %s ]\n", verbuf);
		}
		free(vers);
	    }
	    else if (quietflg == 0)
		fprintf(stderr, "[ Warning: Unknown block signature %s ]\n",
			block_id);
	}
	len -= blk->size + 4;
	scan += blk->size + 4;
    }


    if (outfab != 0)
    {				/* Do not link XABPRO,XABRDT now. Leave them for sys$close() */

	outfab->fab$l_xab = 0L;
	if (xabfhc != 0L)
	{
	    xabfhc->xab$l_nxt = outfab->fab$l_xab;
	    outfab->fab$l_xab = xabfhc;
	}
	if (xabdat != 0L)
	{
	    xabdat->xab$l_nxt = outfab->fab$l_xab;
	    outfab->fab$l_xab = xabdat;
	}
	if (first_xab != 0L)	/* Link xaball,xabkey subchain */
	{
	    last_xab->xab$l_nxt = outfab->fab$l_xab;
	    outfab->fab$l_xab = first_xab;
	}
	return 1;
    }
    else
	return 0;
}

static void free_up()
{				/*
				*	Free up all allocated xabs
				*/
    if (xabdat != 0L) free(xabdat);
    if (xabpro != 0L) free(xabpro);
    if (xabrdt != 0L) free(xabrdt);
    if (xabfhc != 0L) free(xabfhc);
    while (first_xab != 0L)
    {
	struct XAB *x;

	x = first_xab->xab$l_nxt;
	free(first_xab);
	first_xab = x;
    }
    if (outfab != 0L && outfab != &fileblk)
	free(outfab);
}

static int get_vms_version(verbuf, len)
    char *verbuf;
int len;
{
    int i = SYI$_VERSION;
    int verlen = 0;
    struct dsc$descriptor version;
    char *m;

    version.dsc$a_pointer = verbuf;
    version.dsc$w_length = len - 1;
    version.dsc$b_dtype = DSC$K_DTYPE_B;
    version.dsc$b_class = DSC$K_CLASS_S;

    if (ERR(lib$getsyi(&i, 0, &version, &verlen, 0, 0)) || verlen == 0)
	return 0;

    /* Cut out trailing spaces "V5.4-3   " -> "V5.4-3" */
    for (m = verbuf + verlen, i = verlen - 1; i > 0 && verbuf[i] == ' '; --i)
	--m;
    *m = 0;

    /* Cut out release number "V5.4-3" -> "V5.4" */
    if ((m = strrchr(verbuf, '-')) != NULL)
	*m = 0;
    return strlen(verbuf) + 1;	/* Transmit ending 0 too */
}

/******************************
 *   Function extract_block   *
 ******************************/
/*
 * Extracts block from p. If resulting length is less then needed, fill
 * extra space with corresponding bytes from 'init'.
 * Currently understands 3 formats of block compression:
 * - Simple storing
 * - Compression of zero bytes to zero bits
 * - Deflation. See memextract() from extract.c
 */
static byte *extract_block(p, retlen, init, needlen)
    struct extra_block *p;
int *retlen;
byte *init;
int needlen;
{
    byte *block;		/* Pointer to block allocated */
    int cmptype;
    int usiz, csiz, max;

    cmptype = p->flags & BC_MASK;
    csiz = p->size - EXTBSL - RESL;
    usiz = (cmptype == BC_STORED ? csiz : p->length);

    if (needlen == 0)
	needlen = usiz;

    if (retlen)
	*retlen = usiz;

#ifndef MAX
#define MAX(a,b)	( (a) > (b) ? (a) : (b) )
#endif

    if ((block = (byte *) malloc(MAX(needlen, usiz))) == NULL)
	return NULL;

    if (init && (usiz < needlen))
	memcpy(block, init, needlen);

    switch (cmptype)
    {
	case BC_STORED:	/* The simplest case */
	    memcpy(block, &(p->body[0]), usiz);
	    break;
	case BC_00:
	    decompress_bits(block, usiz, &(p->body[0]));
	    break;
	case BC_DEFL:
	    memextract(block, usiz, &(p->body[0]), csiz);
	    break;
	default:
	    free(block);
	    block = NULL;
    }
    return block;
}

/*
 *  Simple uncompression routine. The compression uses bit stream.
 *  Compression scheme:
 *
 *  if(byte!=0)
 *      putbit(1),putbyte(byte)
 *  else
 *      putbit(0)
 */
static void decompress_bits(outptr, needlen, bitptr)
    byte *bitptr;

/* Pointer into compressed data */
byte *outptr;			/* Pointer into output block */
int needlen;			/* Size of uncompressed block */
{
    ULONG bitbuf = 0;
    int bitcnt = 0;

#define _FILL   if(bitcnt+8 <= 32)                      \
                {       bitbuf |= (*bitptr++) << bitcnt;\
                        bitcnt += 8;                    \
                }

    while (needlen--)
    {
	if (bitcnt <= 0)
	    _FILL;

	if (bitbuf & 1)
	{
	    bitbuf >>= 1;
	    if ((bitcnt -= 1) < 8)
		_FILL;
	    *outptr++ = (byte) bitbuf;
	    bitcnt -= 8;
	    bitbuf >>= 8;
	}
	else
	{
	    *outptr++ = 0;
	    bitcnt -= 1;
	    bitbuf >>= 1;
	}
    }
}

/***************************/
/*  Function FlushOutput() */
/***************************/

int FlushOutput()
{
    if (mem_mode)
    {				/* Hope, mem_mode stays constant during
				 * extraction */
	int rc = FlushMemory();	/* For mem_extract() */

	outpos += outcnt;
	outcnt = 0;
	outptr = outbuf;
	return rc;
    }

    /* return PK-type error code */
    /* flush contents of output buffer */
    if (tflag)
    {				/* Do not output. Update CRC only */
	UpdateCRC(outbuf, outcnt);
	outpos += outcnt;
	outcnt = 0;
	outptr = outbuf;
	return 0;
    }
    else
	return text_file ? _flush_records(0) : _flush_blocks(0);
}

static int _flush_blocks(final_flag)	/* Asynchronous version */
    int final_flag;

/* 1 if this is the final flushout */
{
    int round;
    int rest;
    int off = 0;
    int out_count = outcnt;
    int status;

    while (out_count > 0)
    {
	if (curbuf->bufcnt < BUFS512)
	{
	    int ncpy;

	    ncpy = out_count > (BUFS512 - curbuf->bufcnt) ?
			    BUFS512 - curbuf->bufcnt :
			    out_count;
	    memcpy(curbuf->buf + curbuf->bufcnt, outbuf + off, ncpy);
	    out_count -= ncpy;
	    curbuf->bufcnt += ncpy;
	    off += ncpy;
	}
	if (curbuf->bufcnt == BUFS512)
	{
	    status = WriteBuffer(curbuf->buf, curbuf->bufcnt);
	    if (status)
		return status;
	    curbuf = curbuf->next;
	    curbuf->bufcnt = 0;
	}
    }

    UpdateCRC(outbuf, outcnt);
    outpos += outcnt;
    outcnt = 0;
    outptr = outbuf;

    return (final_flag && (curbuf->bufcnt > 0)) ?
	WriteBuffer(curbuf->buf, curbuf->bufcnt) :
	0;
    /* 0:  no error */
}

#define RECORD_END(c) ((c) == CR || (c) == LF || (c) == CTRLZ)

static int _flush_records(final_flag)
    int final_flag;

/* 1 if this is the final flushout */
{
    int rest;
    int end = 0, start = 0;
    int off = 0;

    if (outcnt == 0 && loccnt == 0)
	return 0;		/* Nothing to do ... */

    if (loccnt)
    {
	for (end = 0; end < outcnt && !RECORD_END(outbuf[end]);)
	    ++end;

	if (end >= outcnt && !final_flag)
	{
	    if (WriteRecord(locbuf, loccnt))
		return (50);
	    fprintf(stderr, "[ Warning: Record too long (%d) ]\n",
		    outcnt + loccnt);
	    memcpy(locbuf, outbuf, outcnt);
	    locptr = &locbuf[loccnt = outcnt];
	}
	else
	{
	    memcpy(locptr, outbuf, end);
	    if (WriteRecord(locbuf, loccnt + end))
		return (50);
	    loccnt = 0;
	    locptr = &locbuf;
	}
	start = end + 1;

	if (start < outcnt && outbuf[end] == CR && outbuf[start] == LF)
	    ++start;
    }

    do
    {
	while (start < outcnt && outbuf[start] == CR)	/* Skip CR's at the
							*  beginning of rec. */
	    ++start;
	/* Find record end */
	for (end = start; end < outcnt && !RECORD_END(outbuf[end]);)
	    ++end;

	if (end < outcnt)
	{			/* Record end found, write the record */
	    if (WriteRecord(outbuf + start, end - start))
		return (50);
	    /* Shift to the begining of the next record */
	    start = end + 1;
	}

	if (start < outcnt && outbuf[end] == CR && outbuf[start] == LF)
	    ++start;

    } while (start < outcnt && end < outcnt);

    rest = outcnt - start;

    if (rest > 0)
	if (final_flag)
	{
	    /* This is a final flush. Put out all remaining in
	    *  the buffer                               */
	    if (loccnt && WriteRecord(locbuf, loccnt))
		return (50);
	}
	else
	{
	    memcpy(locptr, outbuf + start, rest);
	    locptr += rest;
	    loccnt += rest;
	}
    UpdateCRC(outbuf, outcnt);
    outpos += outcnt;
    outcnt = 0;
    outptr = outbuf;
    return (0);			/* 0:  no error */
}

/***************************/
/*  Function WriteBuffer() */
/***************************/

static int WriteBuffer(buf, len)/* return 0 if successful, 1 if not */
    unsigned char *buf;
int len;
{
    int status;

    status = sys$wait(outrab);
#ifdef DEBUG
    if (ERR(status))
    {
	message("[ WriteBuffer: sys$wait failed ]\n", status);
	message("", outrab->rab$l_stv);
    }
#endif
    outrab->rab$w_rsz = len;
    outrab->rab$l_rbf = buf;

    if (ERR(status = sys$write(outrab)))
    {
	message("[ WriteBuffer: sys$write failed ]\n", status);
	message("", outrab->rab$l_stv);
	return 50;
    }
    return (0);
}

/***************************/
/*  Function WriteRecord() */
/***************************/

static int WriteRecord(rec, len)/* return 0 if successful, 1 if not */
    unsigned char *rec;
int len;
{
    int status;

    sys$wait(outrab);
#ifdef DEBUG
    if (ERR(status))
    {
	message("[ WriteRecord: sys$wait faled ]\n", status);
	message("", outrab->rab$l_stv);
    }
#endif
    outrab->rab$w_rsz = len;
    outrab->rab$l_rbf = rec;

    if (ERR(status = sys$put(outrab)))
    {
	message("[ WriteRecord: sys$put failed ]\n", status);
	message("", outrab->rab$l_stv);
	return 50;
    }
    return (0);
}

/********************************/
/*  Function CloseOutputFile()  */
/********************************/

int CloseOutputFile()
{
    int status;

    if (text_file) _flush_records(1);
    else
	_flush_blocks(1);
    /* Link XABRDT,XABDAT and optionaly XABPRO */
    if (xabrdt != 0L)
    {
	xabrdt->xab$l_nxt = 0L;
	outfab->fab$l_xab = xabrdt;
    }
    else
    {
	rdt.xab$l_nxt = 0L;
	outfab->fab$l_xab = &rdt;
    }
    if (xabdat != 0L)
    {
	xabdat->xab$l_nxt = outfab->fab$l_xab;
	outfab->fab$l_xab = xabdat;
    }
    if (secinf && xabpro != 0L)
    {
	xabpro->xab$l_nxt = outfab->fab$l_xab;
	outfab->fab$l_xab = xabpro;
    }

    sys$wait(outrab);

    status = sys$close(outfab);
#ifdef DEBUG
    if (ERR(status))
    {
	message("\r[ Warning: cannot set owner/protection/time attributes ]\n", status);
	message("", outfab->fab$l_stv);
    }
#endif
    free_up();
}

#ifdef DEBUG
dump_rms_block(p)
    unsigned char *p;
{
    unsigned char bid, len;
    int err;
    char *type;
    char buf[132];
    int i;

    err = 0;
    bid = p[0];
    len = p[1];
    switch (bid)
    {
	case FAB$C_BID:
	    type = "FAB";
	    break;
	case XAB$C_ALL:
	    type = "xabALL";
	    break;
	case XAB$C_KEY:
	    type = "xabKEY";
	    break;
	case XAB$C_DAT:
	    type = "xabDAT";
	    break;
	case XAB$C_RDT:
	    type = "xabRDT";
	    break;
	case XAB$C_FHC:
	    type = "xabFHC";
	    break;
	case XAB$C_PRO:
	    type = "xabPRO";
	    break;
	default:
	    type = "Unknown";
	    err = 1;
	    break;
    }
    printf("Block @%08X of type %s (%d).", p, type, bid);
    if (err)
    {
	printf("\n");
	return;
    }
    printf(" Size = %d\n", len);
    printf(" Offset - Hex - Dec\n");
    for (i = 0; i < len; i += 8)
    {
	int j;

	printf("%3d - ", i);
	for (j = 0; j < 8; j++)
	    if (i + j < len)
		printf("%02X ", p[i + j]);
	    else
		printf("   ");
	printf(" - ");
	for (j = 0; j < 8; j++)
	    if (i + j < len)
		printf("%03d ", p[i + j]);
	    else
		printf("    ");
	printf("\n");
    }
}

#endif				/* DEBUG */

static void message(string, status)
    int status;
char *string;
{
    char msgbuf[256];

    $DESCRIPTOR(msgd, msgbuf);
    int msglen = 0;

    if (ERR(lib$sys_getmsg(&status, &msglen, &msgd, 0, 0)))
	fprintf(stderr, "%s[ VMS status = %d ]\n", string, status);
    else
    {
	msgbuf[msglen] = 0;
	fprintf(stderr, "%s[ %s ]\n", string, msgbuf);
    }
}


#endif				/* !ZIPINFO */
#endif				/* VMS */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.