diff options
author | gad <gad@FreeBSD.org> | 2009-06-24 16:57:33 +0000 |
---|---|---|
committer | gad <gad@FreeBSD.org> | 2009-06-24 16:57:33 +0000 |
commit | 9a6a7d8ce26144dca1e9a288a4457749f465514a (patch) | |
tree | 901f0b263379b969c0acde87a5cb307136d31351 | |
parent | 8680a0da13e7d0054f547e794e0db7ea71121a01 (diff) | |
download | FreeBSD-src-9a6a7d8ce26144dca1e9a288a4457749f465514a.zip FreeBSD-src-9a6a7d8ce26144dca1e9a288a4457749f465514a.tar.gz |
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
-rw-r--r-- | usr.sbin/lpr/common_source/displayq.c | 103 |
1 files changed, 102 insertions, 1 deletions
diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c index 6c03621..62df9d3 100644 --- a/usr.sbin/lpr/common_source/displayq.c +++ b/usr.sbin/lpr/common_source/displayq.c @@ -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 Owner Job Files"; 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 |