/*
 *		scopy
 *
 * Replace the named files with identical copies such that the
 * replacement file has "vanilla RMS" file attributes.
 * No option preserves 8-bit data, but strips NUL bytes.
 * The -b option transfers the file as read (not recommended as
 *	attributes get messed up).
 * The -a option cleans up random ascii.
 * The -p option strips "parity", NUL, and DEL codes
 *
 */

/*)BUILD
*/

#ifdef	DOCUMENTATION

title	scopy	Make file standard RMS
index		Make file standard RMS

synopsis

	scopy [options] file_list

description

	Scopy creates an identical copy of the named input files
	(the file_list may contain wild cards), creating output
	files with standard file attributes (record type = variable
	length, record format = implied carriage control).

	If there are no arguments, scopy prompts for the option and
	then for each file.

	Options:
	.lm +8
	.p -8
	-a	Cleanup random ASCII, removing NUL, DEL, and other junk.

	-b	Binary, don't remove anything (not even NUL bytes).
	This option is not recommended as scopy doesn't preserve
	attributes.

	-p	Remove the 8'th "parity" bit.
	.lm -8
	If no option is given, data is 8-bit, but NUL bytes are
	removed.  Also, <cr><lf> is mapped to <nl>.

diagnostics

	Scopy does not run on RSTS, RT11, or Unix as these operating systems
	do not support multiple versions of the same file.

author

	Martin Minow

bugs

#endif

#include <stdio.h>
#define	FALSE		0
#define	TRUE		1
#define	EOS		0
#include <ctype.h>
#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif

static int	verbose	= TRUE;
static char	filename[81];		/* Just file name		*/
static char	text[81];		/* Work buffer for interactive	*/
static char	record[2048 + 1];	/* Work buffer for file copy	*/
static FILE	*infd;
static FILE	*outfd;
char		aflag = FALSE;		/* Clean up ASCII if TRUE	*/
char		bflag = FALSE;		/* Binary if TRUE		*/
char		pflag = FALSE;		/* Strip 8'th bit if TRUE	*/

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

	/*
	 * scopy will overwrite files if there are no version numbers
	 */
#ifdef	unix
	fprintf(stderr, "This program must not be used on Unix\n");
	exit(IO_ERROR);
#endif
#ifdef	rt11
	fprintf(stderr, "This program must not be used on RT11\n");
	exit(IO_ERROR);
#endif
#ifdef	rsx
	extern int	$$rsts;

	if ($$rsts) {
	    fprintf(stderr, "This program must not be used on RSTS/E\n");
	    exit(IO_ERROR);
	}
#endif
#ifdef vms
	argc = getredirection(argc, argv);
#endif
	for (i = j = 1; i < argc; i++) {
	    ap = argv[i];
	    if (*ap != '-')
		argv[j++] = ap;
	    else {
		for (++ap; (c = *ap++) != EOS; ) {
		    c = tolower(c);
		    switch (c) {
		    case 'a':
			aflag = TRUE;
			break;

		    case 'b':
			bflag = TRUE;
			break;

		    case 'p':
			pflag = TRUE;
			break;

		    default:
			fprintf(stderr,	"Unknown option '%c', ignored\n", *ap);
			break;

		    }
		}				/* For all options	*/
	    }					/* If -arg		*/
	}					/* For all arg's	*/
	argc = j;				/* New end of table	*/
	if (bflag && (aflag | pflag)) {
	    fprintf(stderr, "Illegal option combination, -b stands alone\n");
	    exit(IO_ERROR);
	}
	if (argc <= 1)
	    interactive();
	else {
	    for (i = 1; i < argc; i++)
		dofiles(argv[i]);
	}
}

interactive()
/*
 * Prompt for files to do
 */
{
	register char	*tp;

	fprintf(stderr,
	    "Replace file by an identical copy, changing output\n");
	fprintf(stderr,
	    "file attributes to variable-length, implied carriage control\n");
	fprintf(stderr,
	    "File names may contain wild-cards\n");
	if (!aflag) {
	    for (;;) {
		fprintf(stderr,
		    "Clear out extraneous junk characters (Yes/No) <No>? ");
		fflush(stderr);
		if (gets(text) == NULL)
		    return;
		if (match(text, "yes")) {
		    aflag = TRUE;
		    break;
		}
		else if (text[0] == EOS || match(text, "no")) {
		    aflag = FALSE;
		    break;
		}
		else {
		    fprintf(stderr,
			"Can't understand \"%s\", please answer Yes or No\n",
			text);
		}
	    }
	}
	while (!feof(stdin)) {
	    fprintf(stderr, "File: ");
	    fflush(stderr);
	    gets(text);
	    if (feof(stdin))
		break;
	    if (text[0] != EOS)
		dofiles(text);
	}
}

dofiles(wildname)
char		*wildname;
/*
 * Do all files for this wild_card argument
 */
{
	register	int	nfiles;
	register	long	nrecords;
	int			count;

#ifdef decus
	if ((infd = fwild(wildname, (bflag) ? "rn" : "r")) == NULL) {
	    fprintf(stderr, "Illegal (wild-card) file name: \"%s\"\n",
		wildname);
	    return;
	}
#else
	if ((infd = fwild(wildname, "r")) == NULL) {
	    fprintf(stderr, "Illegal (wild-card) file name: \"%s\"\n",
		wildname);
	    return;
	}
#endif
	for (nfiles = 0; fnext(infd) != NULL; nfiles++) {
	    setname(infd);
#ifdef decus
	    outfd = fopen(filename, (bflag) ? "wn" : "w");
#else
#ifdef vms
	    if (bflag)
		outfd = fopen(filename, "w");
	    else {
		int unit;
		if ((unit = creat(filename, 0, "rat=cr", "rfm=var")) == -1)
		    outfd = NULL;
		else
		    outfd = fdopen(unit, "w");
	    }
#else
	    outfd = fopen(filename, "w");		/* Unix		*/
#endif
#endif
	    if (outfd == NULL) {
		perror(filename);
		continue;
	    }
	    nrecords = 0;
	    while ((count = readrecord(infd)) >= 0) {
		nrecords++;
		if (bflag)
		    fwrite(record, count, 1, outfd);
		else
		    fputs(record, outfd);
	    }
	    if (ferror(infd) || ferror(outfd))
		perror(filename);
	    fclose(outfd);
	    if (verbose) {
		printf("%8ld records: %s\n", nrecords, filename);
	    }
	}
	if (nfiles == 0) {
	    fprintf(stderr, "No files matching \"%s\"\n", wildname);
	}
}

int
readrecord(fd)
register FILE	*fd;
/*
 * Read a line, watch out for garbage.  Returns size or -1 on eof/error
 */
{
	register char	*rp;
	register int	c;

	if (feof(fd))
	    return (-1);
	for (rp = record; rp < &record[(sizeof record)] - 1;) {
	    if ((c = getc(fd)) == EOF)
		break;
	    if (pflag)
		c &= 0x7F;
	    if (!bflag) {
		switch (c) {
		case 0:				/* Always toss NUL	*/
		case 0x7F:			/* Always toss DEL	*/
		    continue;

		case '\f':
		case '\b':
		case '\t':
		case '\n':
		case ('K' - '@'):		/* VT			*/
		case '\007':			/* BELL			*/
		    break;			/* Always accept these	*/

		case '\r':
		    if ((c = getc(fd)) != '\n') {
			ungetc(c, fd);		/* Save next char	*/
			c = '\r';		/* Keeping the <cr>	*/
		    }
		    break;			/* Accept <cr> if naked	*/

		default:
		    if (aflag && c < ' ')
			continue;		/* Toss random controls	*/
		    break;
		}
	    }
	    *rp++ = c;				/* Got a character	*/
	    if (!bflag && c == '\n')
		break;				/* End of line		*/
	}
	*rp = EOS;				/* For debugging	*/
	return (rp - record);			/* Number gotten	*/
}

setname(fd)
FILE 		*fd;
/*
 * Put output file name into filename buffer
 */
{
	register char		*ptr;

	fgetname(fd, filename);
	if ((ptr = strchr(filename, ';')) != NULL)
	    *ptr = EOS;			/* Remove version number	*/
}

int
match(arg, value)
register char		*arg;
register char		*value;
/*
 * Return TRUE if arg matches leftmost part of value.
 * Value must be in lowercase.
 */
{
	register char	c;

	while ((c = tolower(*arg++)) != EOS) {
	    if (c != *value++)
		return (FALSE);
	}
	return (TRUE);
}

bug(severity, mess, arg)
char		*severity;
char		*mess;
char		*arg;
/*
 * Error messages
 */
{
	fprintf(stderr, "?SCAT-%s-%s", severity, mess);
	if (arg != NULL)
		fprintf(stderr, ": \"%s\"", arg);
	fprintf(stderr, "\n");
	if (*severity != 'W')
		error("?SCAT-F-Can't continue");
}


dumptext(buffer, count, fd)
register char		*buffer;
int			count;
FILE			*fd;
{
	extern char	*dumpchar();

	putc('"', fd);
	while (--count >= 0)
	    fputs(dumpchar((int) *buffer++), fd);
	fputs("\"\n", fd);
}

char *
dumpchar(c)
register int	c;
/*
 * Make a character printable.  Returns a static pointer.
 */
{
	static char	dump_buffer[8];

	c &= 0xFF;
	if (isascii(c) && isprint(c)) {
	    dump_buffer[0] = c;
	    dump_buffer[1] = EOS;
	}
	else {
	    switch (c) {
	    case '\n':	return ("\\n");
	    case '\t':	return ("\\t");
	    case '\b':	return ("\\b");
	    case '\f':	return ("\\f");
	    case '\r':	return ("\\r");
	    }
	    sprintf(dump_buffer, "<x%02X>", c);
	}
	return (dump_buffer);
}


