/*
 *				l i n e p r . c
 * Print with line numbers.
 */

/*)BUILD
*/

#ifdef	DOCUMENTATION

title	linepr	Print files with line numbers and headers
index		Print files with line numbers and headers

synopsis

	linepr [-options] [-o outfile] [file ...]

description

	linepr prepares a listing for text files.  The
	following options are defined:
	.lm +8
	.s.i -8;-s	Spool output to the default line printer.
	(On RSX modes only).
	.s.i -8;-o file	Write output to the named file.  The default is the
	first input file encountered with a filetype (extension) of ".x".
	.s.lm -8
	linepr accepts wild-card file input.

diagnostics

	.lm +8
	.s.i -8;Cannot open "file"
	.s.i -8;Cannot open listing file "file"
	.s.i -8;Usage ...
	.lm -8

author

	Robert B. Denny, Martin Minow

bugs
#endif

#include <stdio.h>
#define	EOS	0
#define	TRUE	1
#define	FALSE	0
#define LWIDTH  132		/* Listing width, characters		*/
#define MAXLIN  57		/* Listing lines per page (w/o header)	*/
#ifdef	vms
/*
 * This creates files in vanilla RMS on VMS V2
 */
extern FILE *fdopen();
static FILE *
CREATE(filename, mode)
char		*filename, *mode;
{
	int		channel;

	if ((channel = creat(filename, 0, "rat=cr", "rfm=var")) == -1)
	    return (NULL);
	else
	    return (fdopen(channel, mode));
}
#else
#define	CREATE	fopen
#endif

#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

FILE	*src;			/* Source file pointer			*/
FILE	*lst;			/* List file pointer			*/
int	pageno;			/* Current listing page no.		*/
int	lineno = 0;		/* Current source line number		*/
int 	linpg = 0;		/* Line-in-page count			*/
int	was_lf;			/* TRUE if last line ended with NL	*/
int	ccpos;			/* Where on the line			*/
char	line[LWIDTH+1];		/* Source line buffer			*/
char	infilname[80];		/* Current input file			*/
int	time_of_day[2];		/* Time of day in seconds		*/
char	timetext[26];		/* Gets time of day in Ascii		*/

main(argc, argv)
int		argc;
char		*argv[];
{
	register int	i;		/* Arg count			*/
	register int	c;		/* Switch character code	*/
	register char	*ap;		/* Fast arg pointer		*/
	char		*in_file;	/* Source file name string	*/
	char		*out_file;	/* Cref file name string	*/
#ifdef	rsx
	int		splflg;		/* Spool file flag		*/
#endif
	int		nofiles;	/* Flag "got a file"		*/
	int		nofound;	/* Flag "got a wildcard file"	*/

#ifdef vms
	/*
	 * VAX C doesn't do redirection.  Actually, we don't use stdin or
	 * stdout for anything, so the only thing we gain by having the
	 * call here is that users can try and redirect input or output
	 * and won't get any random errors for their efforts.  Also,
	 * this lets you redirect (or cancel) the "file/page" log.
	 */
	argc = getredirection(argc,argv);
#endif
	out_file = NULL;			/* No output file yet	*/
#ifdef	rsx
	splflg = 0;				/* Default no spool	*/
#endif
	for (i=1; i < argc; i++) {		/* For each token	*/
	    ap = argv[i];			/* ap --> i'th arg	*/
	    if (*ap++ == '-') {			/* if it's an option	*/
		while((c = *ap++) != EOS) {	/* Look at each char	*/
		    switch (tolower(c)) {	/* Process 'em		*/
		    case 'o':			/* Output file		*/
			if (*ap != EOS)
			    usage("-o may not be followed by another option");
			argv[i] = NULL;
			if (++i >= argc)
			    usage("-o must be followed by a filename");
			out_file = argv[i];
			argv[i] = NULL;
			goto nextarg;

#ifdef	rsx
		    case 's':			/* Spool listing	*/
			splflg++;
			break;
#endif

		    default:			/* Bad option, help	*/
			usage("Unknown option");
		    }
		}
	        argv[i] = NULL;			/* Drop this arg	*/
	    }
nextarg:;
	}					/* Done with cmd string	*/
	nofiles = TRUE;
	for (i = 1; i < argc; i++) {
	    if ((in_file = argv[i]) != NULL) {
		initinfile(in_file);
		for (nofound = TRUE; nextinfile(); nofound = FALSE) {
		    if (nofiles) {
			initoutfile(out_file);
			nofiles = FALSE;
		    }
		    if (isatty(fileno(stderr)))
			fprintf(stderr, "\n%s\t", infilname);
		    pageno = 0;
		    lineno = 0;
		    newpage();
		    while(fgets(line, LWIDTH, src) != NULL) {
			lstline();
		    }
		    fprintf(lst, "\n");		/* Force terminal NL	*/
		    if (isatty(fileno(stderr)))
			fprintf(stderr, "%3d pages", pageno);
		}
		if (nofound)
		    fprintf(stderr, "\nNo such file: \"%s\"\n", in_file);
	    }
	}
	if (nofiles)
	    usage("no input files given, no output generated");
	if (isatty(fileno(stderr)))
	    putc('\n', stderr);
#ifdef rsx
	if (splflg)				/* If we're to spool,	*/
	    fspool(lst);			/* Cross your fingers.	*/
#endif
	exit(IO_SUCCESS);
}

/*
 * Listing control routines.
 */

newpage()					/* Start new page	*/
{
	if (pageno != 0)
	    fprintf(lst, "\n");			/* End last line	*/
	++pageno;
	linpg = 0;
	fprintf(lst, "\fListing of %s\t%s\tPage %d\n\n",
	    infilname, timetext, pageno);
	was_lf = TRUE;
	ccpos = 0;
}

lstline()					/* Write out list line	*/
{
	register char	*lp;
	register char	*start;
	extern char	*strchr();

	start = line;
	if ((lp = strchr(line, '\f')) == NULL) {
	    if (!was_lf) {
		if (length(line) >= LWIDTH) {
		    fprintf(lst, "\n");
		    ccpos = 0;
		}
		fprintf(lst, "%s", line);
	    }
	    else {
		if (++linpg > MAXLIN)
		    newpage();
		fprintf(lst, "%4d:\t%s", lineno + 1, line);
	    }
	}
	else {
	    if (lp > line) {			/* Before formfeed	*/
		*lp = EOS;			/* Eat the formfeed	*/
		if (!was_lf)
		    fprintf(lst, "%s\n", line);
		else
		    fprintf(lst, "%4d:\t%s\n", lineno + 1, line);
		start = "\n";
	    }
	    newpage();
	    if (*++lp != '\n' && *lp != EOS) {	/* Stuff after formfeed	*/
		++linpg;
		fprintf(lst, "%4d:\t%s", lineno + 1, lp);
		start = lp;
	    }
	}
	if (was_lf = (start[strlen(start) - 1] == '\n')) {
	    lineno++;
	    ccpos = 0;
	}
	else {
	    length(start);
	}
}

int
length(lp)
register char	*lp;		/* Text pointer				*/
/*
 * Calculate new ccpos value (slightly incorrect at newline)
 * Returns new ccpos value.
 */
{
	register char	*newlp;
	extern char	*strchr();

	while ((newlp = strchr(lp, '\t')) != NULL) {
	    ccpos += (newlp - lp);	/* Before the TAB		*/
	    ccpos = (ccpos & 7) + 8;	/* The TAB itself		*/
	    lp = newlp + 1;		/* Point lp after the TAB	*/
	}
	ccpos += strlen(lp);		/* All the rest			*/
}

/*
 * Set up RSX file names, open them, and initialize the page header strings
 */

initinfile(in)
char		*in;
{
	if ((src = fwild(in, "r")) == NULL) {
	    perror(in);
	    error("Cannot open %s\n", in);
	}
	if (time_of_day[0] == 0) {
	    time(&time_of_day);
	}
}

initoutfile(out)
char	*out;
{
	char wrkbuf[80];
	char firstin[80];

	if (out != NULL) {
	    name(wrkbuf, out, "lst", 0);
	}
	else {
	    fgetname(src, firstin);
	    name(wrkbuf, firstin, "lst", 1);          /* Make list file name */
	}
	if ((lst = CREATE(wrkbuf, "w")) == NULL) {
	    perror(wrkbuf);
	    error("Cannot open listing file %s\n", wrkbuf);
	}
}

int
nextinfile()
{
	register char		*wp;
	register int		c;
	extern char		*strrchr();

	if (fnext(src) == NULL)
	    return (0);
	/*
	 * Get true input file name, drop dev::disk on VMS/RSX
	 * or /dir/dir/ on Unix
	 */
	fgetname(src, infilname);
#ifdef unix
	if ((wp = strrchr(infilname, '/') != NULL)
	    strcpy(infilname, wp + 1);
#else
	if ((wp = strrchr(infilname, ':')) != NULL)
	    strcpy(infilname, wp + 1);
#endif
	strcpy(timetext, ctime(&time_of_day));
	timetext[24] = EOS;			/* Trash newline	*/
	return (1);
}

/*
 * Make a file name.
 * The mode argument is either 0 which means use type as default extension,
 * or 1, which means force the extension to be type. Argument file is the
 * 'raw' file name, sysnam is the final filename string. All arg's except
 * mode are pointers, as usual.
 *
 */

name(sysnam, file, type, mode)
char	*sysnam;
char	*file;
char	*type;
int	mode;
{
	register char *p1, *p2;		/* Fast pointers		*/
	register int c;			/* Fast char. buffer		*/

	p1 = sysnam;				/* p1 --> output string */
	p2 = file;				/* p2 --> input string */

	while ((c = *p2++) != EOS) {
	    if (c == '[') {
		*p1++ = c;
		while ((c = *p2++) != ']' && c != EOS)
		    *p1++ = c;
	    }
	    if (c == '.')
		break;
	    *p1++ = c;
	}
	if (mode == 0) {			/* Default extension	*/
	    if (c == '.') {			/* Explicit extension	*/
		do {				/* Copy '.' + any ext.	*/
		    *p1++ = c;
		} while ((c = *p2++) != EOS);
	    }
	    else {				/* Use default		*/
		*p1++ = '.';
		p2 = type;
		while (c = *p2++)
		    *p1++ = c;
	    }
	}
	else {					/* Force extension	*/
	    *p1++ = '.';
	    p2 = type;
	    while (c = *p2++)
		*p1++ = c;
	}
	*p1 = EOS;				/* Terminate result	*/
}

usage(why)
char	*why;
/*
 * Give user help on program useage.
 */
{
#ifdef	vms
	fprintf(stderr, "\nlinepr error: %s.\n", why);
	fprintf(stderr, "Usage: linepr [-o outfile] infiles\n");
#else
	extern int	$$rsts;

	fprintf(stderr, "\nlinepr error: %s.\n", why);
#ifdef rt11
	if ($$rsts) {
	    fprintf(stderr, "Usage: CRUN LINEPR [-s] [-o outfile] infiles\n");
	    fprintf(stderr, "       -s  Spool output file\n");
	}
	else {
	    fprintf(stderr, "Usage (short): .RUN LINEPR infile\n");
	    fprintf(stderr, "Usage  (long): .RUN LINEPR\n");
	    fprintf(stderr, "               Argv: [-o outfile] infiles\n");
	}
#else
	fprintf(stderr, "Usage: linepr [-s] [-o outfile] infiles\n");
	fprintf(stderr, "       -s  Spool output file\n");
#endif
#endif
	fprintf(stderr, "       -o  Output to \"outfile\"\n\n");
#ifdef	rsx
	fprintf(stderr, "Input files may have RSX wild-card names\n");
#else
#ifdef	rt11
	fprintf(stderr, "Input files may have RT11 wild-card names\n");
#else
	fprintf(stderr, "Input files may have wild-card names\n");
#endif
#endif
	fprintf(stderr, "Default input filetype is \".c\"\n");
	fprintf(stderr,
	    "Default output filename is \"<first_input_file>.lst\"\n");
	exit(IO_ERROR);
}

