From sean-freebsd@farley.org  Tue Oct 24 03:04:55 2006
Return-Path: <sean-freebsd@farley.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 3D41316A4A7
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 24 Oct 2006 03:04:55 +0000 (UTC)
	(envelope-from sean-freebsd@farley.org)
Received: from mail.farley.org (farley.org [67.64.95.201])
	by mx1.FreeBSD.org (Postfix) with ESMTP id C633843D55
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 24 Oct 2006 03:04:53 +0000 (GMT)
	(envelope-from sean-freebsd@farley.org)
Received: from thor.farley.org (thor.farley.org [192.168.1.5])
	by mail.farley.org (8.13.8/8.13.8) with ESMTP id k9O36RSX052462
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 23 Oct 2006 22:06:27 -0500 (CDT)
	(envelope-from sean-freebsd@mail.farley.org)
Received: from thor.farley.org (localhost [127.0.0.1])
	by thor.farley.org (8.13.8/8.13.8) with ESMTP id k9O34lM7002734
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 23 Oct 2006 22:04:47 -0500 (CDT)
	(envelope-from sean-freebsd@thor.farley.org)
Received: (from sean@localhost)
	by thor.farley.org (8.13.8/8.13.8/Submit) id k9O34lOq002733;
	Mon, 23 Oct 2006 22:04:47 -0500 (CDT)
	(envelope-from sean-freebsd)
Message-Id: <200610240304.k9O34lOq002733@thor.farley.org>
Date: Mon, 23 Oct 2006 22:04:47 -0500 (CDT)
From: Sean Farley <sean-freebsd@farley.org>
Reply-To: Sean Farley <sean-freebsd@farley.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [patch] missing newline in lpq output
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         104731
>Category:       bin
>Synopsis:       [patch] missing newline in lpq(1) output
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    gad
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 24 03:10:20 GMT 2006
>Closed-Date:    Fri Jul 24 21:00:30 UTC 2009
>Last-Modified:  Fri Jul 24 21:00:30 UTC 2009
>Originator:     Sean Farley
>Release:        FreeBSD 6.2-PRERELEASE i386
>Organization:
>Environment:
System: FreeBSD thor.farley.org 6.2-PRERELEASE FreeBSD 6.2-PRERELEASE #1: Sun Oct 1 12:38:47 CDT 2006 root@thor.farley.org:/usr/obj/usr/src/sys/THOR i386

>Description:
When lpq outputs the status from a remote printer/lpd, it prints exactly
what is given to it.  With an HP 3055 that has no jobs, the output is
"queue empty" without the newline.

>How-To-Repeat:
lpq run against a printer or lpd that does not send a closing newline.

>Fix:
I am providing two patches.  The first simply prints a newline following
a complete output from the printer.  The second checks for a newline and
prints one only if the last character was not a newline.  I prefer the
second patch, but the first is better than nothing.

There is a second spot in the display code, but I am not certain that it
requires the same type of patch.

The patches will also be available at:
http://www.farley.org/freebsd/tmp/PR/<PR #>/lpq-patch1.txt
http://www.farley.org/freebsd/tmp/PR/<PR #>/lpq-patch2.txt

------ First patch ------
diff -ru /usr/src/usr.sbin/lpr/common_source/displayq.c lpr/common_source/displayq.c
--- /usr/src/usr.sbin/lpr/common_source/displayq.c	Thu Dec 30 18:36:28 2004
+++ lpr/common_source/displayq.c	Mon Oct 23 20:44:15 2006
@@ -255,6 +255,7 @@
 			fatal(pp, "Lost connection");
 		while ((i = read(fd, line, sizeof(line))) > 0)
 			(void) fwrite(line, 1, i, stdout);
+		fprintf(stdout, "\n");
 		(void) close(fd);
 	}
 }
------ First patch ------


------ Second patch ------
diff -ru /usr/src/usr.sbin/lpr/common_source/displayq.c lpr/common_source/displayq.c
--- /usr/src/usr.sbin/lpr/common_source/displayq.c	Thu Dec 30 18:36:28 2004
+++ lpr/common_source/displayq.c	Mon Oct 23 20:54:32 2006
@@ -96,6 +96,7 @@
 {
 	register struct jobqueue *q;
 	register int i, nitems, fd, ret;
+	int lastChar;
 	char *cp, *endp;
 	struct jobqueue **queue;
 	struct stat statb;
@@ -253,8 +254,13 @@
 		i = strlen(line);
 		if (write(fd, line, i) != i)
 			fatal(pp, "Lost connection");
-		while ((i = read(fd, line, sizeof(line))) > 0)
+		lastChar = 0;
+		while ((i = read(fd, line, sizeof(line))) > 0) {
 			(void) fwrite(line, 1, i, stdout);
+			lastChar = i - 1;
+		}
+		if (lastChar > 0 && line[lastChar] != '\n')
+			fprintf(stdout, "\n");
 		(void) close(fd);
 	}
 }
------ Second patch ------
>Release-Note:
>Audit-Trail:

From: "Sean C. Farley" <sean-freebsd@farley.org>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/104731: [patch] missing newline in lpq output
Date: Tue, 24 Oct 2006 18:35:03 -0500 (CDT)

 For the second patch, use the patch at
 http://www.farley.org/freebsd/tmp/PR/104731/lpq-patch2.txt since it
 handles the case where the printer sends back a single character upon
 the last read().
Responsible-Changed-From-To: freebsd-bugs->gad 
Responsible-Changed-By: gad 
Responsible-Changed-When: Tue Oct 31 03:26:13 UTC 2006 
Responsible-Changed-Why:  
I'll look into this, and do something similar to your lpq-patch2.txt, 
although it won't be quite the same.  Note that if the while(i=read()) 
loop is executed more than once, then your lastChar check may write 
out an extra 'n' line.  Also, when I've seen this problem, I've also 
seen that the remote-host is in fact sending back 'r' characters, 
and I'd want to do something a bit more intelligent in that case. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=104731 

From: "Sean C. Farley" <sean-freebsd@farley.org>
To: FreeBSD-gnats-submit@FreeBSD.org, gad@FreeBSD.org
Cc:  
Subject: Re: bin/104731: [patch] missing newline in lpq output
Date: Wed, 1 Nov 2006 18:55:56 -0600 (CST)

 On Tue, 31 Oct 2006, gad@FreeBSD.org wrote:
 
 > I'll look into this, and do something similar to your lpq-patch2.txt,
 > although it won't be quite the same. Note that if the while(i=read())
 > loop is executed more than once, then your lastChar check may write
 > out an extra 'n' line.
 
 Assuming you are looking at the patch off of my web site, I do not see
 how an extra '\n' line would be output.  lastChar is set for every
 successful read with data.  Only the last successful read with data is
 checked for '\n'.
 
 > Also, when I've seen this problem, I've also seen that the remote-host
 > is in fact sending back 'r' characters, and I'd want to do something a
 > bit more intelligent in that case.
 
 I agree that '\r' should also be checked.  I forgot about it.  :)
 
 Of course, a better solution is always welcome; I only have one printer
 in my data set.
 
 Sean
 
 P.S. I never received an e-mail from gnats with your reply.  Is it
 supposed to send one?
 -- 
 sean-freebsd@farley.org

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/104731: commit references a PR
Date: Wed, 24 Jun 2009 16:57:43 +0000 (UTC)

 Author: gad
 Date: Wed Jun 24 16:57:33 2009
 New Revision: 194859
 URL: http://svn.freebsd.org/changeset/base/194859
 
 Log:
   Fix end-of-line issues that can come up when `lpq' reads information
   about a queue from a remote host.  That remote host may use \r, \r\n,
   or \n\r as the line-ending character.  In some cases the remote host
   will write a single line of information without *any* EOL sequence.
   
   Translate all the non-unix EOL's to the standard newline, and make
   sure the final line includes a terminating newline.  Logic is also
   added to translate all unprintable characters to '?', but that is
   #if-ed out for now.
   
   PR:		bin/104731
   MFC after:	3 weeks
 
 Modified:
   head/usr.sbin/lpr/common_source/displayq.c
 
 Modified: head/usr.sbin/lpr/common_source/displayq.c
 ==============================================================================
 --- head/usr.sbin/lpr/common_source/displayq.c	Wed Jun 24 16:52:30 2009	(r194858)
 +++ head/usr.sbin/lpr/common_source/displayq.c	Wed Jun 24 16:57:33 2009	(r194859)
 @@ -69,6 +69,13 @@ __FBSDID("$FreeBSD$");
  #define SIZCOL	62		/* start of Size column in normal */
  
  /*
 + * isprint() takes a parameter of 'int', but expect values in the range
 + * of unsigned char.  Define a wrapper which takes a value of type 'char',
 + * whether signed or unsigned, and ensure it ends up in the right range.
 + */
 +#define	isprintch(Anychar) isprint((u_char)(Anychar))
 +
 +/*
   * Stuff for handling job specifications
   */
  extern uid_t	uid, euid;
 @@ -86,6 +93,7 @@ static const char  *head0 = "Rank   Owne
  static const char  *head1 = "Total Size\n";
  
  static void	alarmhandler(int _signo);
 +static void	filtered_write(char *_obuffer, int _wlen, FILE *_wstream);
  static void	warn(const struct printer *_pp);
  
  /*
 @@ -254,12 +262,105 @@ displayq(struct printer *pp, int format)
  		if (write(fd, line, i) != i)
  			fatal(pp, "Lost connection");
  		while ((i = read(fd, line, sizeof(line))) > 0)
 -			(void) fwrite(line, 1, i, stdout);
 +			filtered_write(line, i, stdout);
 +		filtered_write(NULL, -1, stdout);
  		(void) close(fd);
  	}
  }
  
  /*
 + * The lpq-info read from remote hosts may contain unprintable characters,
 + * or carriage-returns instead of line-feeds.  Clean those up before echoing
 + * the lpq-info line(s) to stdout.  The info may also be missing any kind of
 + * end-of-line character.  This also turns CRLF and LFCR into a plain LF.
 + *
 + * This routine may be called multiple times to process a single set of
 + * information, and after a set is finished this routine must be called
 + * one extra time with NULL specified as the buffer address.
 + */
 +static void
 +filtered_write(char *wbuffer, int wlen, FILE *wstream)
 +{
 +	static char lastchar, savedchar;
 +	char *chkptr, *dest_end, *dest_ch, *nxtptr, *w_end;
 +	int destlen;
 +	char destbuf[BUFSIZ];
 +
 +	if (wbuffer == NULL) {
 +		if (savedchar != '\0') {
 +			if (savedchar == '\r')
 +				savedchar = '\n';
 +			fputc(savedchar, wstream);
 +			lastchar = savedchar;
 +			savedchar = '\0';
 +		}
 +		if (lastchar != '\0' && lastchar != '\n')
 +			fputc('\n', wstream);
 +		lastchar = '\0';
 +		return;
 +	}
 +
 +	dest_ch = &destbuf[0];
 +	dest_end = dest_ch + sizeof(destbuf);
 +	chkptr = wbuffer;
 +	w_end = wbuffer + wlen;
 +	lastchar = '\0';
 +	if (savedchar != '\0') {
 +		chkptr = &savedchar;
 +		nxtptr = wbuffer;
 +	} else
 +		nxtptr = chkptr + 1;
 +
 +	while (chkptr < w_end) {
 +		if (nxtptr < w_end) {
 +			if ((*chkptr == '\r' && *nxtptr == '\n') ||
 +			    (*chkptr == '\n' && *nxtptr == '\r')) {
 +				*dest_ch++ = '\n';
 +				/* want to skip past that second character */
 +				nxtptr++;
 +				goto check_next;
 +			}
 +		} else {
 +			/* This is the last byte in the buffer given on this
 +			 * call, so check if it could be the first-byte of a
 +			 * significant two-byte sequence.  If it is, then
 +			 * don't write it out now, but save for checking in
 +			 * the next call.
 +			 */
 +			savedchar = '\0';
 +			if (*chkptr == '\r' || *chkptr == '\n') {
 +				savedchar = *chkptr;
 +				break;
 +			}
 +		}
 +		if (*chkptr == '\r')
 +			*dest_ch++ = '\n';
 +#if 0		/* XXX - don't translate unprintable characters (yet) */
 +		else if (*chkptr != '\t' && *chkptr != '\n' &&
 +		    !isprintch(*chkptr))
 +			*dest_ch++ = '?';
 +#endif
 +		else
 +			*dest_ch++ = *chkptr;
 +
 +check_next:
 +		chkptr = nxtptr;
 +		nxtptr = chkptr + 1;
 +		if (dest_ch >= dest_end) {
 +			destlen = dest_ch - &destbuf[0];
 +			fwrite(destbuf, 1, destlen, wstream);
 +			lastchar = destbuf[destlen - 1];
 +			dest_ch = &destbuf[0];
 +		}
 +	}
 +	destlen = dest_ch - &destbuf[0];
 +	if (destlen > 0) {
 +		fwrite(destbuf, 1, destlen, wstream);
 +		lastchar = destbuf[destlen - 1];
 +	}
 +}
 +
 +/*
   * Print a warning message if there is no daemon present.
   */
  static void
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/104731: commit references a PR
Date: Fri, 24 Jul 2009 20:36:04 +0000 (UTC)

 Author: gad
 Date: Fri Jul 24 20:35:44 2009
 New Revision: 195855
 URL: http://svn.freebsd.org/changeset/base/195855
 
 Log:
   MFC:  Fix end-of-line issues that can come up when `lpq' reads information
   about a queue from a remote host.
   
   PR:		bin/104731
 
 Modified:
   stable/7/usr.sbin/lpr/   (props changed)
   stable/7/usr.sbin/lpr/common_source/displayq.c
 
 Modified: stable/7/usr.sbin/lpr/common_source/displayq.c
 ==============================================================================
 --- stable/7/usr.sbin/lpr/common_source/displayq.c	Fri Jul 24 19:35:06 2009	(r195854)
 +++ stable/7/usr.sbin/lpr/common_source/displayq.c	Fri Jul 24 20:35:44 2009	(r195855)
 @@ -69,6 +69,13 @@ __FBSDID("$FreeBSD$");
  #define SIZCOL	62		/* start of Size column in normal */
  
  /*
 + * isprint() takes a parameter of 'int', but expect values in the range
 + * of unsigned char.  Define a wrapper which takes a value of type 'char',
 + * whether signed or unsigned, and ensure it ends up in the right range.
 + */
 +#define	isprintch(Anychar) isprint((u_char)(Anychar))
 +
 +/*
   * Stuff for handling job specifications
   */
  extern uid_t	uid, euid;
 @@ -86,6 +93,7 @@ static const char  *head0 = "Rank   Owne
  static const char  *head1 = "Total Size\n";
  
  static void	alarmhandler(int _signo);
 +static void	filtered_write(char *_obuffer, int _wlen, FILE *_wstream);
  static void	warn(const struct printer *_pp);
  
  /*
 @@ -254,12 +262,105 @@ displayq(struct printer *pp, int format)
  		if (write(fd, line, i) != i)
  			fatal(pp, "Lost connection");
  		while ((i = read(fd, line, sizeof(line))) > 0)
 -			(void) fwrite(line, 1, i, stdout);
 +			filtered_write(line, i, stdout);
 +		filtered_write(NULL, -1, stdout);
  		(void) close(fd);
  	}
  }
  
  /*
 + * The lpq-info read from remote hosts may contain unprintable characters,
 + * or carriage-returns instead of line-feeds.  Clean those up before echoing
 + * the lpq-info line(s) to stdout.  The info may also be missing any kind of
 + * end-of-line character.  This also turns CRLF and LFCR into a plain LF.
 + *
 + * This routine may be called multiple times to process a single set of
 + * information, and after a set is finished this routine must be called
 + * one extra time with NULL specified as the buffer address.
 + */
 +static void
 +filtered_write(char *wbuffer, int wlen, FILE *wstream)
 +{
 +	static char lastchar, savedchar;
 +	char *chkptr, *dest_end, *dest_ch, *nxtptr, *w_end;
 +	int destlen;
 +	char destbuf[BUFSIZ];
 +
 +	if (wbuffer == NULL) {
 +		if (savedchar != '\0') {
 +			if (savedchar == '\r')
 +				savedchar = '\n';
 +			fputc(savedchar, wstream);
 +			lastchar = savedchar;
 +			savedchar = '\0';
 +		}
 +		if (lastchar != '\0' && lastchar != '\n')
 +			fputc('\n', wstream);
 +		lastchar = '\0';
 +		return;
 +	}
 +
 +	dest_ch = &destbuf[0];
 +	dest_end = dest_ch + sizeof(destbuf);
 +	chkptr = wbuffer;
 +	w_end = wbuffer + wlen;
 +	lastchar = '\0';
 +	if (savedchar != '\0') {
 +		chkptr = &savedchar;
 +		nxtptr = wbuffer;
 +	} else
 +		nxtptr = chkptr + 1;
 +
 +	while (chkptr < w_end) {
 +		if (nxtptr < w_end) {
 +			if ((*chkptr == '\r' && *nxtptr == '\n') ||
 +			    (*chkptr == '\n' && *nxtptr == '\r')) {
 +				*dest_ch++ = '\n';
 +				/* want to skip past that second character */
 +				nxtptr++;
 +				goto check_next;
 +			}
 +		} else {
 +			/* This is the last byte in the buffer given on this
 +			 * call, so check if it could be the first-byte of a
 +			 * significant two-byte sequence.  If it is, then
 +			 * don't write it out now, but save for checking in
 +			 * the next call.
 +			 */
 +			savedchar = '\0';
 +			if (*chkptr == '\r' || *chkptr == '\n') {
 +				savedchar = *chkptr;
 +				break;
 +			}
 +		}
 +		if (*chkptr == '\r')
 +			*dest_ch++ = '\n';
 +#if 0		/* XXX - don't translate unprintable characters (yet) */
 +		else if (*chkptr != '\t' && *chkptr != '\n' &&
 +		    !isprintch(*chkptr))
 +			*dest_ch++ = '?';
 +#endif
 +		else
 +			*dest_ch++ = *chkptr;
 +
 +check_next:
 +		chkptr = nxtptr;
 +		nxtptr = chkptr + 1;
 +		if (dest_ch >= dest_end) {
 +			destlen = dest_ch - &destbuf[0];
 +			fwrite(destbuf, 1, destlen, wstream);
 +			lastchar = destbuf[destlen - 1];
 +			dest_ch = &destbuf[0];
 +		}
 +	}
 +	destlen = dest_ch - &destbuf[0];
 +	if (destlen > 0) {
 +		fwrite(destbuf, 1, destlen, wstream);
 +		lastchar = destbuf[destlen - 1];
 +	}
 +}
 +
 +/*
   * Print a warning message if there is no daemon present.
   */
  static void
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: gad 
State-Changed-When: Fri Jul 24 20:56:18 UTC 2009 
State-Changed-Why:  
The fix for this has been committed to HEAD and to STABLE/7, which is 
as much as I plan to MFC it... 

http://www.freebsd.org/cgi/query-pr.cgi?pr=104731 
>Unformatted:
