/*
 * ckvcvt - assorted outboard processing for C-Kermit/VMS labeled files
 */

/* Revision History:
 * T1.0-00 - 08-Apr-91 - tmk - Initial coding, sync w/ 5A(167) and ckvfio.c
 *			       2.0-066.
 * T1.0-01 - 14-Apr-91 - tmk - Fix errors in help output, show actual system
 *			       message on RMS error.
 * T1.0-02 - 15-Apr-91 - tmk - Redefine fab$b_journal as fab$b_rfm+1.
 * T1.0-03 - 15-Apr-91 - tmk - ACL support, sync w/ 5A(169) and ckvfio.c 2.0-
 *			       069.
 * T1.0-04 - 16-Apr-91 - tmk - Fix _another_ journaling bug (whimper), handle
 *			       missing semicolon in filespec gracefully.
 * T1.0-05 - 04-Sep-92 - tmk - Implement ckvfio.c 087 fix.
 * T1.0-06 - 08-Apr-93 - tmk - Implement ckvfio.c 097 fix.
 * T1.0-07 - 24-Feb-95 - mpjz- Fix for DEC C on VAX.
 * T1.0-08 - 06-Sep-95 - fdc - Get rid of nonportable memmove().
 */

#ifdef WINTCP
#include stdio
#include stdlib
#include ctype
#include string
#include rms
#include ssdef
#else
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <rms.h>
#include <ssdef.h>
#endif /* WINTCP */
#define	VERSION	"T1.0-06"

#ifndef OLD_VMS
#include <starlet.h>
#endif /* OLD_VMS */

#define	R_MODE	"rb"
#define	IO_ERROR	0
#define IO_SUCCESS	1

/*
 * Definitions for output file
 */

static struct FAB fab_ofile;
static struct RAB rab_ofile;
static struct XABDAT xabdat_ofile;
static struct XABFHC xabfhc_ofile;
static struct XABPRO xabpro_ofile;
static struct XABALL xaball_ofile;
static struct XABRDT xabrdt_ofile;
static short ofile_ffb;
static int ofile_rec;

/*
 * Common RMS items
 */

static unsigned long int rms_sts;

/*
 * Global varables
 */

int	keepacl = 0;			/* Preserve ACL data? */
int	backup	= 0;			/* Preserve file backup date? */
int	debug	= 0;			/* Debug output? */
int	notrim	= 0;			/* Don't trim names? */
int	inquire = 0;			/* Testing if labeled? */
int	owner	= 0;			/* Preserve file ownership? */
int	strip	= 0;			/* Just stripping? */
FILE	*infd	= NULL;			/* Input file descriptor */
char	*infn	= NULL;			/* Input file name */
char	*outfn	= NULL;			/* Special output file name */
char	buffer[512];			/* Work buffer */
char	label[99];			/* Label name */
int	lblen	= 0;			/* Length of label */
char	vmsname[255];			/* Stored name */
char	vmsfile[70];			/* Stored file info */
char	vmsacl[512];			/* Stored ACL data */
int	acllen = 0;			/* Size of same */
char	*filptr = vmsfile;		/* Attribute pointer */
int	gotname	= 0;			/* Found name? */
int	gotfile = 0;			/* Found file info? */
int	gotacl = 0;			/* Found ACL info? */
int	bail	= 0;			/* If we should bail out */
char	revdat[8];			/* Revision date */
unsigned short revnum;			/* Revision number */
unsigned short jnlflg;			/* Journaling flags */

/*
 * Function prototypes
 */

extern int main(int, char **);
void do_help();
void barf(char *);
void strip_file();

#if !defined(__DECC) && !defined(VAX)
#define XABP char
#else
#define XABP void
#endif

/* memmove() replacement */

void
mymove(to, from, len) char * to; char * from; int len; {
    char tmp[16384];
    strncpy((char *)tmp,from,len);
    strncpy(to,(char *)tmp,len);
}

/*
 * Ok, let's do it
 */

int main(argc, argv)
int argc;
char *argv[];
{
    register char *ap;
    int i, j;					/* How original */

    if (argc < 2)				/* User say anything? */
	do_help();				/* If not... */

    if (*argv[1] != '-') {
	infn = argv[1];
	if ((infd = fopen(argv[1], R_MODE)) == NULL) {
	    perror(argv[1]);
	    exit(IO_ERROR);
	}
	argc--;
	argv++;
    }
    else
	bail++;

    while (argc > 1) {
	ap = argv[1];
	if (*ap != '-') {
	    fprintf(stderr, "Unknown option '%s',", ap);
	    fprintf(stderr, " do CKVCVT -? for help.\n");
	}
	else for (ap++; *ap; ap++) {
	    switch (tolower(*ap)) {

	    case 'a':				/* Preserve ACL's */
		keepacl++;
		break;

	    case 'b':				/* Preserve backup date */
		backup++;
		break;

	    case 'd':				/* Debug mode? */
		debug++;
		break;

	    case 'f':				/* Name output file */
		if (isgraph(ap[1]) != 0)
		    outfn = &ap[1];
		else if (argc > 2) {
		    outfn = argv[2];
		    argc--;
		    argv++;
		}
		else {
		    break;
		}
		goto next_arg;

	    case 'i':				/* Inquire if labeled */
		inquire++;
		strip++;
		break;

	    case 'o':				/* Preserve file ownership */
		owner++;
		break;

	    case 's':				/* Just strip it */
		strip++;
		break;

	    case 't':				/* Don't trim filenames */
		notrim++;
		break;

	    case '?':				/* Emit help text */
	    case 'h':
		do_help();
		break;

	    default:
		fprintf(stderr, "?Unknown option '%c',", *ap);
		fprintf(stderr, " do CKVCVT -? for help\n");
	    }
	}
	next_arg:
	argc--;
	argv++;
    }

    if (bail != 0)
	exit(IO_ERROR);

    fread(buffer, 20, 1, infd);
    if (strncmp(buffer, "KERMIT LABELED FILE:", 20) != 0)
	barf("not a Kermit labeled file");

    fread(buffer, 2, 1, infd);
    buffer[2] = '\0';
    lblen = atoi(buffer);
    if (lblen != 2)
	barf("");

    fread(buffer, lblen, 1, infd);

    if (strip != 0)				/* Stripping headers? */
	strip_file();

    if (strncmp(buffer, "D7", 2) != 0)
	barf("not from a VAX/VMS system");

    fread(buffer, 6, 1, infd);
    if (strncmp(buffer, "04VERS", 6) != 0)
	barf("");

    fread(buffer, 8, 1, infd);
    buffer[8] = '\0';
    lblen = atoi(buffer);

    fread(buffer, lblen, 1, infd);
    buffer[lblen] = '\0';
    if (debug)
	fprintf(stderr, "File created under VAX/VMS %s\n", buffer);

    fread(buffer, 7, 1, infd);
    if (strncmp(buffer, "05KVERS", 7) != 0)
	barf("");

    fread(buffer, 8, 1, infd);
    buffer[8] = '\0';
    lblen = atoi(buffer);

    fread(buffer, lblen, 1, infd);
    buffer[lblen] = '\0';
    if (debug)
	fprintf(stderr, "File created with C-Kermit/VMS %s\n", buffer);

    next_label:
    fread(buffer, 2, 1, infd);
    buffer[2] = '\0';
    lblen = atoi(buffer);
    if (lblen == 0)
	barf("lost sync");

    fread(buffer, lblen, 1, infd);
    buffer[lblen] = '\0';
    if (strcmp(buffer, "VMSNAME") == 0) {
	fread(buffer, 8, 1, infd);
	buffer[8] = '\0';
	lblen = atoi(buffer);
	fread(vmsname, lblen, 1, infd);
	vmsname[lblen] = '\0';
	gotname++;
	if (debug)
	    fprintf(stderr, "Loaded file name block as %s\n", vmsname);
	i = (int)strstr(vmsname, "::");
	if (i != (int)NULL) {
	    char temp[255];
	    i += 2;
	    mymove(vmsname, (char *)i, (int)strlen(vmsname));
	}
        if (!notrim) {
	    char temp[255];
	    i = (int)strrchr(vmsname, ':');
	    j = (int)strrchr(vmsname, ']');
	    if (j == (int)NULL)
		j = (int)strrchr(vmsname, '>');
	    if (j > i)
		i = j;
	    i++;
	    mymove(vmsname, (char *)i, (int) strlen(vmsname));
	}
	if (strchr(vmsname, ';') != NULL) {
	    for (j = strlen(vmsname); vmsname[j] != ';'; j--)
	    ;
	    vmsname[j] = '\0';
	}
	if (debug)
	    fprintf(stderr, "Resultant filespec: %s\n", vmsname);
	goto next_label;
    }
    else if (strcmp(buffer, "VMSFILE") == 0) {
	fread(buffer, 8, 1, infd);
	buffer[8] = '\0';
	lblen = atoi(buffer);
	fread(vmsfile, lblen, 1, infd);
	vmsfile[lblen] = '\0';
	gotfile++;
	if (debug)
	    fprintf(stderr, "Loaded file attribute block\n");
	goto next_label;
    }
    else if (strcmp(buffer, "VMSACL") == 0) {
	fread(buffer, 8, 1, infd);
	buffer[8] = '\0';
	acllen = atoi(buffer);
	fread(vmsacl, acllen, 1, infd);
	vmsacl[acllen] = '\0';
	gotacl++;
	if (debug)
	    fprintf(stderr, "Loaded file ACL block\n");
	goto next_label;
    }
    else if (strcmp(buffer, "DATA") == 0) {
	fread(buffer, 8, 1, infd);
	buffer[8] = '\0';
	lblen = atoi(buffer);
	if (lblen != 0)
	    barf("");
	if (debug)
	    fprintf(stderr, "Positioned at start of file data\n");
	goto all_set;
    }
    else {
	fprintf(stderr, "%s: unrecognized label '%s'\n", infn, buffer);
	fread(buffer, 8, 1, infd);
	buffer[8] = '\0';
	lblen = atoi(buffer);
	if (lblen > 512)
	    barf("unrecognized label too long to skip");
	fread(vmsfile, lblen, 1, infd);
	goto next_label;
    }

    all_set:
    if (gotfile != 1 || gotname != 1)
	barf("missing required labels");

/*
 * Prep the characteristics
 */

    fab_ofile = cc$rms_fab;
    fab_ofile.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
    fab_ofile.fab$l_fop = FAB$M_MXV;
    if (outfn == NULL) {
	fab_ofile.fab$l_fna = vmsname;
	fab_ofile.fab$b_fns = strlen(vmsname);
    }
    else {
	fab_ofile.fab$l_fna = outfn;
	fab_ofile.fab$b_fns = strlen(outfn);
    }
    fab_ofile.fab$l_xab = (XABP *)&xabdat_ofile;
    rab_ofile = cc$rms_rab;
    rab_ofile.rab$l_fab = &fab_ofile;
    xabdat_ofile = cc$rms_xabdat;
    xabdat_ofile.xab$l_nxt = (XABP *)&xabrdt_ofile;
    xabrdt_ofile = cc$rms_xabrdt;
    xabrdt_ofile.xab$l_nxt = (XABP *)&xabfhc_ofile;
    xabfhc_ofile = cc$rms_xabfhc;
    xabfhc_ofile.xab$l_nxt = (XABP *)&xabpro_ofile;
    xabpro_ofile = cc$rms_xabpro;
    xabpro_ofile.xab$l_nxt = (XABP *)&xaball_ofile;
    xaball_ofile = cc$rms_xaball;

/*
 * Load 'em up
 */

    mymove(&xabpro_ofile.xab$w_pro, filptr, 2);
    filptr += 2;
    if (owner != 0)
	mymove(&xabpro_ofile.xab$l_uic, filptr, 4);
    filptr += 4;
    mymove(&fab_ofile.fab$b_rfm, filptr, 1);
    filptr += 1;
    mymove(&fab_ofile.fab$b_org, filptr, 1);
    filptr += 1;
    mymove(&fab_ofile.fab$b_rat, filptr, 1);
    filptr += 5;				/* 4 bytes reserved for char */
    mymove(&fab_ofile.fab$b_fsz, filptr, 1);
    filptr += 1;
    mymove(&xabfhc_ofile.xab$w_lrl, filptr, 2);
    filptr += 2;
    mymove(&fab_ofile.fab$w_mrs, filptr, 2);
    filptr += 2;
    mymove(&xabfhc_ofile.xab$l_ebk, filptr, 4);
    filptr += 4;
/* preserve this as RMS won't remember it for us */
    mymove(&ofile_ffb, filptr, 2);
    filptr += 2;
    mymove(&xaball_ofile.xab$l_alq, filptr, 4);
    filptr += 4;
    mymove(&xaball_ofile.xab$w_deq, filptr, 2);
    filptr += 2;
#ifdef COMMENT /* was: defined(VAX) && defined(__DECC) */
/*
   This is really annoying. The people from DEC changed xaball, but only on
   VAX not AXP!  - mpjz
*/
    mymove(&xaball_ofile.xaballdef$$_fill_7, filptr, 1);
#else
    mymove(&xaball_ofile.xab$b_bkz, filptr, 1);
#endif /* COMMENT */
    filptr += 1;
    mymove(&fab_ofile.fab$w_gbc, filptr, 2);
    filptr += 2;
    mymove(&xabfhc_ofile.xab$w_verlimit, filptr, 2);
    filptr += 2;
    mymove(&jnlflg, filptr, 1);
    if (jnlflg !=0)
      printf(
	     "Original file was marked for RMS Journaling, this copy is not.\n"
	     );
    filptr += 1;
    mymove(&xabdat_ofile.xab$q_cdt, filptr, 8);
    filptr += 8;
    mymove(&revdat, filptr, 8);
    filptr += 8;
    mymove(&revnum, filptr, 2);
    filptr += 2;
    mymove(&xabdat_ofile.xab$q_edt, filptr, 8);
    filptr += 8;
    if (backup != 0)
	mymove(&xabdat_ofile.xab$q_bdt, filptr, 8);
    filptr += 8;

/*
 * ACL's?
 */

    if(keepacl != 0 && gotacl != 0) {
	xabpro_ofile.xab$l_aclbuf = (XABP *)&vmsacl;
	xabpro_ofile.xab$w_aclsiz = acllen;
    }

/*
 * Give it a quick whirl around the dance floor
 */

    printf("Creating %s...\n", fab_ofile.fab$l_fna);
    rms_sts = sys$create(&fab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);

    if(keepacl != 0 && gotacl != 0) {
	if (!(xabpro_ofile.xab$l_aclsts & 1))
	    exit(xabpro_ofile.xab$l_aclsts);
    }

    rms_sts = sys$connect(&rab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);

    ofile_rec = 1;
    fread(buffer, 512, 1, infd);
    while (!feof(infd)) {
	rab_ofile.rab$l_rbf = buffer;
	rab_ofile.rab$w_rsz = 512;
	if (ofile_rec == xabfhc_ofile.xab$l_ebk) {
	    xabfhc_ofile.xab$w_ffb = ofile_ffb;
	    if (ofile_ffb)
		rab_ofile.rab$w_rsz -= (512 - ofile_ffb);
	    if (debug) {
		fprintf(stderr,"FFB (first free byte) = %d\n", ofile_ffb);
		fprintf(stderr,"Last record size = %d\n", rab_ofile.rab$w_rsz);
	    }
	}
	rms_sts = sys$write(&rab_ofile);
	if (!(rms_sts & 1))
	    exit(rms_sts);
	fread(buffer, 512, 1, infd);
	ofile_rec++;
    }

/*
 * Update the revision information
 */

    mymove(&xabrdt_ofile.xab$q_rdt, revdat, 8);
    mymove(&xabrdt_ofile.xab$w_rvn, &revnum, 2);

    rms_sts = sys$close(&fab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);
    printf("...done.\n");
    exit(IO_SUCCESS);
}

void do_help()
{
    printf("This is CKVCVT %s\n\n", VERSION);
    printf("Usage: CKVCVT infile options\n\n");
    printf("Options:\n");
    printf("         -a     Preserve file ACL data (may require privs)\n");
    printf("         -b     Preserve file backup date\n");
    printf("         -d     Print debugging information\n");
    printf("         -f fn  Name output file fn instead of default\n");
    printf("         -i     Inquire if a file is labeled\n");
    printf("         -o     Preserve file ownership (requires privs)\n");
    printf("         -s     Strip one level of label information\n");
    printf("         -t     Don't trim paths from output file name\n");
    printf("         -?     Display this message\n\n");
    exit(IO_SUCCESS);
}

void barf(text)
char *text;
{
    if (text == "")
	fprintf(stderr, "%s: corrupted Kermit labeled file\n", infn);
    else
	fprintf(stderr, "%s: %s\n", infn, text);
    exit(IO_ERROR);
}

void strip_file()
{
    next_label:
    fread(buffer, 2, 1, infd);			/* Get label length */
    buffer[2] = '\0';
    lblen = atoi(buffer);
    if (lblen == 0)				/* Better not be zero! */
	barf("lost sync");

    fread(label, lblen, 1, infd);		/* Get the label name */
    label[lblen] = '\0';
    fread(buffer, 8, 1, infd);			/* Get the contents length */
    buffer[8] = '\0';
    lblen = atoi(buffer);
    if (strcmp(label, "DATA") == 0) {		/* If done... */
	if (lblen != 0)
	    barf("");
	if (debug)
	    fprintf(stderr, "Positioned at start of file data\n");
	goto all_set;
    }
    fread(buffer, lblen, 1, infd);		/* Else skip contents */
    goto next_label;				/* And loop */

/*
 * We've skipped the first header, now do whatever task is needed
 */

    all_set:
    if (inquire != 0) {
	printf("%s is a properly formatted Kermit labeled file\n", infn);
	exit(IO_SUCCESS);
    }

    fab_ofile = cc$rms_fab;
    fab_ofile.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
    fab_ofile.fab$l_fop = FAB$M_MXV;
    fab_ofile.fab$l_fna = infn;
    fab_ofile.fab$b_fns = strlen(infn);
    fab_ofile.fab$b_rfm = FAB$C_FIX;
    fab_ofile.fab$w_mrs = 512;
    rab_ofile = cc$rms_rab;
    rab_ofile.rab$l_fab = &fab_ofile;

    printf("Stripping %s...\n", fab_ofile.fab$l_fna);
    rms_sts = sys$create(&fab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);

    rms_sts = sys$connect(&rab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);

    fread(buffer, 512, 1, infd);
    while (!feof(infd)) {
	rab_ofile.rab$l_rbf = buffer;
	rab_ofile.rab$w_rsz = 512;
	rms_sts = sys$write(&rab_ofile);
	if (!(rms_sts & 1))
	    exit(rms_sts);
	fread(buffer, 512, 1, infd);
    }

    rms_sts = sys$close(&fab_ofile);
    if (!(rms_sts & 1))
	exit(rms_sts);
    printf("...done.\n");
    exit(IO_SUCCESS);
}
