* Original idea and code:
* Walter Harms <WHarms@bfs.de>
*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
*
* See RFC 1179 for protocol description.
*/
+
+//usage:#define lpr_trivial_usage
+//usage: "-P queue[@host[:port]] -U USERNAME -J TITLE -Vmh [FILE]..."
+/* -C CLASS exists too, not shown.
+ * CLASS is supposed to be printed on banner page, if one is requested */
+//usage:#define lpr_full_usage "\n\n"
+//usage: " -P lp service to connect to (else uses $PRINTER)"
+//usage: "\n -m Send mail on completion"
+//usage: "\n -h Print banner page too"
+//usage: "\n -V Verbose"
+//usage:
+//usage:#define lpq_trivial_usage
+//usage: "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID]... [-fs]"
+//usage:#define lpq_full_usage "\n\n"
+//usage: " -P lp service to connect to (else uses $PRINTER)"
+//usage: "\n -d Delete jobs"
+//usage: "\n -f Force any waiting job to be printed"
+//usage: "\n -s Short display"
+
#include "libbb.h"
/*
* LPD returns binary 0 on success.
* Otherwise it returns error message.
*/
-static void get_response_or_say_and_die(const char *errmsg)
+static void get_response_or_say_and_die(int fd, const char *errmsg)
{
- static const char newline = '\n';
- char buf = ' ';
+ ssize_t sz;
+ char buf[128];
- fflush(stdout);
-
- safe_read(STDOUT_FILENO, &buf, 1);
- if ('\0' != buf) {
+ buf[0] = ' ';
+ sz = safe_read(fd, buf, 1);
+ if ('\0' != buf[0]) {
// request has failed
- bb_error_msg("error while %s. Server said:", errmsg);
- safe_write(STDERR_FILENO, &buf, 1);
- logmode = 0; /* no error messages from bb_copyfd_eof() pls */
- bb_copyfd_eof(STDOUT_FILENO, STDERR_FILENO);
- safe_write(STDERR_FILENO, &newline, 1);
+ // try to make sure last char is '\n', but do not add
+ // superfluous one
+ sz = full_read(fd, buf + 1, 126);
+ bb_error_msg("error while %s%s", errmsg,
+ (sz > 0 ? ". Server said:" : ""));
+ if (sz > 0) {
+ // sz = (bytes in buf) - 1
+ if (buf[sz] != '\n')
+ buf[++sz] = '\n';
+ safe_write(STDERR_FILENO, buf, sz + 1);
+ }
xfunc_die();
}
}
int lpqr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
-int lpqr_main(int argc, char *argv[])
+int lpqr_main(int argc UNUSED_PARAM, char *argv[])
{
enum {
OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515"
OPT_U = 1 << 1, // -U username
LPR_V = 1 << 2, // -V: be verbose
- LPR_h = 1 << 3, // -h: want banner printed
+ LPR_h = 1 << 3, // -h: want banner printed
LPR_C = 1 << 4, // -C class: job "class" (? supposedly printed on banner)
LPR_J = 1 << 5, // -J title: the job title for the banner page
LPR_m = 1 << 6, // -m: send mail back to user
const char *server = "localhost"; // server[:port] of printer queue
char *hostname;
// N.B. IMHO getenv("USER") can be way easily spoofed!
- const char *user = bb_getpwuid(NULL, -1, getuid());
+ const char *user = xuid2uname(getuid());
unsigned job;
unsigned opts;
- int old_stdout, fd;
+ int fd;
+
+ queue = getenv("PRINTER");
+ if (!queue)
+ queue = "lp";
// parse options
// TODO: set opt_complementary: s,d,f are mutually exclusive
);
argv += optind;
- // if queue is not specified -> use $PRINTER
- if (!(opts & OPT_P))
- queue = getenv("PRINTER");
- // if queue is still not specified ->
- if (!queue) {
- // ... queue defaults to "lp"
- // server defaults to "localhost"
- queue = "lp";
- // if queue is specified ->
- } else {
+ {
// queue name is to the left of '@'
char *s = strchr(queue, '@');
if (s) {
// do connect
fd = create_and_connect_stream_or_die(server, 515);
- // play with descriptors to save space: fdprintf > printf
- old_stdout = dup(STDOUT_FILENO);
- xmove_fd(fd, STDOUT_FILENO);
//
// LPQ ------------------------
goto command;
// delete job(s)
} else if (opts & LPQ_DELETE) {
- printf("\x5" "%s %s", queue, user);
+ fdprintf(fd, "\x5" "%s %s", queue, user);
while (*argv) {
- printf(" %s", *argv++);
+ fdprintf(fd, " %s", *argv++);
}
bb_putchar('\n');
// dump current jobs status
} else {
cmd = (opts & LPQ_SHORT_FMT) ? 3 : 4;
command:
- printf("%c" "%s\n", cmd, queue);
- bb_copyfd_eof(STDOUT_FILENO, old_stdout);
+ fdprintf(fd, "%c" "%s\n", cmd, queue);
+ bb_copyfd_eof(fd, STDOUT_FILENO);
}
return EXIT_SUCCESS;
bb_error_msg("connected to server");
job = getpid() % 1000;
- // TODO: when do finally we invent char *xgethostname()?!!
- hostname = xzalloc(MAXHOSTNAMELEN+1);
- gethostname(hostname, MAXHOSTNAMELEN);
+ hostname = safe_gethostname();
// no files given on command line? -> use stdin
if (!*argv)
*--argv = (char *)"-";
- printf("\x2" "%s\n", queue);
- get_response_or_say_and_die("setting queue");
+ fdprintf(fd, "\x2" "%s\n", queue);
+ get_response_or_say_and_die(fd, "setting queue");
// process files
do {
+ unsigned cflen;
+ int dfd;
struct stat st;
char *c;
char *remote_filename;
// if data file is stdin, we need to dump it first
if (LONE_DASH(*argv)) {
strcpy(tempfile, "/tmp/lprXXXXXX");
- fd = mkstemp(tempfile);
- if (fd < 0)
- bb_perror_msg_and_die("mkstemp");
- bb_copyfd_eof(STDIN_FILENO, fd);
- xlseek(fd, 0, SEEK_SET);
+ dfd = xmkstemp(tempfile);
+ bb_copyfd_eof(STDIN_FILENO, dfd);
+ xlseek(dfd, 0, SEEK_SET);
*argv = (char*)bb_msg_standard_input;
} else {
- fd = xopen(*argv, O_RDONLY);
+ dfd = xopen(*argv, O_RDONLY);
+ }
+
+ st.st_size = 0; /* paranoia: fstat may theoretically fail */
+ fstat(dfd, &st);
+
+ /* Apparently, some servers are buggy and won't accept 0-sized jobs.
+ * Standard lpr works around it by refusing to send such jobs:
+ */
+ if (st.st_size == 0) {
+ bb_error_msg("nothing to print");
+ continue;
}
/* "The name ... should start with ASCII "cfA",
, (opts & LPR_m) ? user : ""
, remote_filename
);
- // delete possible "\nX\n" patterns
+ // delete possible "\nX\n" (that is, one-char) patterns
c = controlfile;
while ((c = strchr(c, '\n')) != NULL) {
- c++;
- while (c[0] && c[1] == '\n')
- memmove(c, c+2, strlen(c+1)); /* strlen(c+1) == strlen(c+2) + 1 */
+ if (c[1] && c[2] == '\n') {
+ overlapping_strcpy(c, c+2);
+ } else {
+ c++;
+ }
}
// send control file
if (opts & LPR_V)
bb_error_msg("sending control file");
+ /* "Acknowledgement processing must occur as usual
+ * after the command is sent." */
+ cflen = (unsigned)strlen(controlfile);
+ fdprintf(fd, "\x2" "%u c%s\n", cflen, remote_filename);
+ get_response_or_say_and_die(fd, "sending control file");
/* "Once all of the contents have
* been delivered, an octet of zero bits is sent as
* an indication that the file being sent is complete.
* A second level of acknowledgement processing
* must occur at this point." */
- printf("\x2" "%u c%s\n" "%s" "%c",
- (unsigned)strlen(controlfile),
- remote_filename, controlfile, '\0');
- get_response_or_say_and_die("sending control file");
+ full_write(fd, controlfile, cflen + 1); /* writes NUL byte too */
+ get_response_or_say_and_die(fd, "sending control file");
// send data file, with name "dfaXXX"
if (opts & LPR_V)
bb_error_msg("sending data file");
- st.st_size = 0; /* paranoia: fstat may theoretically fail */
- fstat(fd, &st);
- printf("\x3" "%"OFF_FMT"u d%s\n", st.st_size, remote_filename);
- if (bb_copyfd_size(fd, STDOUT_FILENO, st.st_size) != st.st_size) {
+ fdprintf(fd, "\x3" "%"OFF_FMT"u d%s\n", st.st_size, remote_filename);
+ get_response_or_say_and_die(fd, "sending data file");
+ if (bb_copyfd_size(dfd, fd, st.st_size) != st.st_size) {
// We're screwed. We sent less bytes than we advertised.
bb_error_msg_and_die("local file changed size?!");
}
- bb_putchar('\0');
- get_response_or_say_and_die("sending data file");
+ write(fd, "", 1); // send ACK
+ get_response_or_say_and_die(fd, "sending data file");
// delete temporary file if we dumped stdin
if (*argv == (char*)bb_msg_standard_input)
free(remote_filename);
free(controlfile);
+ // say job accepted
+ if (opts & LPR_V)
+ bb_error_msg("job accepted");
+
// next, please!
job = (job + 1) % 1000;
} while (*++argv);