From 98f6cc540138b922ca6c1601fd61b54ad3e3a8a0 Mon Sep 17 00:00:00 2001
From: Garance A Drosehn <gad@FreeBSD.org>
Date: Tue, 23 Apr 2002 00:06:10 +0000
Subject: [PATCH] Implement new printcap option of "rc" aka
 "remote.resend_copies". This is a boolean option, and if it is specified in a
 print queue for a remote host, it causes lpd to resend the data file for each
 copy the user requested on 'lpr -#n'.  This is useful for network printers
 which accept lpd-style jobs, but which ignore the control file (and thus they
 ignore any request for multiple copies).

PR:		25635
Reviewed by:	short review on freebsd-audit
MFC after:	6 days
---
 usr.sbin/lpr/common_source/lp.h       |  1 +
 usr.sbin/lpr/common_source/printcap.c |  1 +
 usr.sbin/lpr/lpd/printjob.c           | 59 ++++++++++++++++++++++-----
 usr.sbin/lpr/lpr/printcap.5           | 20 +++++++++
 4 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h
index a2e0bff7cc4b..6faaf4d89929 100644
--- a/usr.sbin/lpr/common_source/lp.h
+++ b/usr.sbin/lpr/common_source/lp.h
@@ -72,6 +72,7 @@ struct	printer {
 	long	 page_width;	/* PW: page width */
 	long	 page_pwidth;	/* PX: page width in pixels */
 	long	 page_plength;	/* PY: page length in pixels */
+	long	 resend_copies;	/* RC: resend copies to remote host */
 	char	*restrict_grp;	/* RG: restricted group */
 	char	*remote_host;	/* RM: remote machine name */
 	char	*remote_queue;	/* RP: remote printer name */
diff --git a/usr.sbin/lpr/common_source/printcap.c b/usr.sbin/lpr/common_source/printcap.c
index d70c5b100376..d9498936e727 100644
--- a/usr.sbin/lpr/common_source/printcap.c
+++ b/usr.sbin/lpr/common_source/printcap.c
@@ -257,6 +257,7 @@ getprintcap_int(char *bp, struct printer *pp)
 			    &pp->status_file));
 	CHK(capdb_getaltstr(bp, "tr", "job.trailer", 0, &pp->trailer));
 
+	pp->resend_copies = capdb_getaltlog(bp, "rc", "remote.resend_copies");
 	pp->restricted = capdb_getaltlog(bp, "rs", "daemon.restricted");
 	pp->short_banner = capdb_getaltlog(bp, "sb", "banner.short");
 	pp->no_copies = capdb_getaltlog(bp, "sc", "job.no_copies");
diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c
index 3ac859e31a78..59ccccb1124b 100644
--- a/usr.sbin/lpr/lpd/printjob.c
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -146,7 +146,7 @@ static void	 scan_out(struct printer *_pp, int _scfd, char *_scsp,
 		    int _dlm);
 static char	*scnline(int _key, char *_p, int _c);
 static int	 sendfile(struct printer *_pp, int _type, char *_file, 
-		    char _format);
+		    char _format, int _copyreq);
 static int	 sendit(struct printer *_pp, char *_file);
 static void	 sendmail(struct printer *_pp, char *_userid, int _bombed);
 static void	 setty(const struct printer *_pp);
@@ -868,7 +868,7 @@ print(struct printer *pp, int format, char *file)
 static int
 sendit(struct printer *pp, char *file)
 {
-	register int i, err = OK;
+	int dfcopies, err, i;
 	char *cp, last[BUFSIZ];
 
 	/*
@@ -895,6 +895,7 @@ sendit(struct printer *pp, char *file)
 	/*
 	 * pass 1
 	 */
+	err = OK;
 	while (getline(cfp)) {
 	again:
 		if (line[0] == 'S') {
@@ -925,11 +926,14 @@ sendit(struct printer *pp, char *file)
 		} else if (line[0] == 'I') {
 			strlcpy(indent+2, line + 1, sizeof(indent) - 2);
 		} else if (line[0] >= 'a' && line[0] <= 'z') {
+			dfcopies = 1;
 			strcpy(last, line);
-			while ((i = getline(cfp)) != 0)
-				if (strcmp(last, line))
+			while ((i = getline(cfp)) != 0) {
+				if (strcmp(last, line) != 0)
 					break;
-			switch (sendfile(pp, '\3', last+1, *last)) {
+				dfcopies++;
+			}
+			switch (sendfile(pp, '\3', last+1, *last, dfcopies)) {
 			case OK:
 				if (i)
 					goto again;
@@ -945,7 +949,7 @@ sendit(struct printer *pp, char *file)
 			break;
 		}
 	}
-	if (err == OK && sendfile(pp, '\2', file, '\0') > 0) {
+	if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) {
 		(void) fclose(cfp);
 		return(REPRINT);
 	}
@@ -969,13 +973,13 @@ sendit(struct printer *pp, char *file)
  * Return positive if we should try resending.
  */
 static int
-sendfile(struct printer *pp, int type, char *file, char format)
+sendfile(struct printer *pp, int type, char *file, char format, int copyreq)
 {
 	int i, amt;
 	struct stat stb;
 	char *av[15], *filtcmd;
 	char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4];
-	int filtstat, narg, resp, sfd, sfres, sizerr, statrc;
+	int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc;
 
 	statrc = lstat(file, &stb);
 	if (statrc < 0) {
@@ -1102,7 +1106,15 @@ sendfile(struct printer *pp, int type, char *file, char format)
 		lseek(sfd, 0, SEEK_SET);
 	}
 
-	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+	copycnt = 0;
+sendagain:
+	copycnt++;
+
+	if (copycnt < 2)
+		(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+	else
+		(void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size,
+		    file, copycnt);
 	amt = strlen(buf);
 	for (i = 0;  ; i++) {
 		if (write(pfd, buf, amt) != amt ||
@@ -1121,6 +1133,10 @@ sendfile(struct printer *pp, int type, char *file, char format)
 	}
 	if (i)
 		pstatus(pp, "sending to %s", pp->remote_host);
+	/*
+	 * XXX - we should change trstat_init()/trstat_write() to include
+	 *	 the copycnt in the statistics record it may write.
+	 */
 	if (type == '\3')
 		trstat_init(pp, file, job_dfcnt);
 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
@@ -1146,9 +1162,30 @@ sendfile(struct printer *pp, int type, char *file, char format)
 		sfres = REPRINT;
 		goto return_sfres;
 	}
-	if (type == '\3')
+	if (type == '\3') {
 		trstat_write(pp, TR_SENDING, stb.st_size, logname,
-				 pp->remote_host, origin_host);
+		    pp->remote_host, origin_host);
+		/*
+		 * Usually we only need to send one copy of a datafile,
+		 * because the control-file will simply print the same
+		 * file multiple times.  However, some printers ignore
+		 * the control file, and simply print each data file as
+		 * it arrives.  For such "remote hosts", we need to
+		 * transfer the same data file multiple times.  Such a
+		 * a host is indicated by adding 'rc' to the printcap
+		 * entry.
+		 * XXX - Right now this ONLY works for remote hosts which
+		 *	do ignore the name of the data file, because
+		 *	this sends the file multiple times with slight
+		 *	changes to the filename.  To do this right would
+		 *	require that we also rewrite the control file
+		 *	to match those filenames.
+		 */
+		if (pp->resend_copies && (copycnt < copyreq)) {
+			lseek(sfd, 0, SEEK_SET);
+			goto sendagain;
+		}
+	}
 	sfres = OK;
 
 return_sfres:
diff --git a/usr.sbin/lpr/lpr/printcap.5 b/usr.sbin/lpr/lpr/printcap.5
index 0f42f6779132..eef7f3bd39b8 100644
--- a/usr.sbin/lpr/lpr/printcap.5
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -109,6 +109,7 @@ blocks), zero = unlimited
 .It "pw	num	132	page width (in characters)"
 .It "px	num	0	page width in pixels (horizontal)"
 .It "py	num	0	page length in pixels (vertical)"
+.It "rc	bool	false	when sending to a remote host, resend copies (see below)"
 .It "rf	str" Ta Dv NULL Ta No "filter for printing"
 .Tn FORTRAN
 style text files
@@ -157,6 +158,7 @@ Each two-letter capability has a human-readable alternate name.
 .It "pw	page.width"
 .It "px	page.pwidth"
 .It "py	page.plength"
+.It "rc	remote.resend_copies"
 .It "rf	filt.fortran"
 .It "rg	daemon.restrictgrp"
 .It "rm	remote.host"
@@ -325,6 +327,24 @@ will therefore happen before
 .Xr pr 1
 is executed rather than afterwards.
 .Pp
+There are some models of network printers which accept jobs from
+.Xr lpd 8 , but they ignore the control file for a job and simply print
+each data file as it arrives at the printer.
+One side-effect of this behavior is that the printer will ignore any request
+for multiple copies as given with the
+.Fl #
+flag on the
+.Xr lpr 1 command.
+The
+.Cm rc
+entry will cause
+.Xr lpd 8 to resend each data file for each copy that the user
+originally requested.
+Note that the
+.Cm rc
+entry should only be specified on hosts which send jobs directly to
+the printer.
+.Pp
 If
 .Cm lp
 is specified as