diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-22 22:24:48 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-22 22:24:48 +0000 |
commit | 68444b9f0c74e94d219fa40bb4109b4aa2fdd43b (patch) | |
tree | 34b1c0d65ac46498b81df8badd85f9daa0baed33 /printutils | |
parent | 6aa74fcf5bc5f6b209b578754fc098714bc8485d (diff) | |
download | busybox-w32-68444b9f0c74e94d219fa40bb4109b4aa2fdd43b.tar.gz busybox-w32-68444b9f0c74e94d219fa40bb4109b4aa2fdd43b.tar.bz2 busybox-w32-68444b9f0c74e94d219fa40bb4109b4aa2fdd43b.zip |
lpr and lpq applets by Walter Harms.
text data bss dec hex filename
392 0 0 392 188 lpq.o
1378 0 0 1378 562 lpr.o
142 0 0 142 8e parse_prt.o
Diffstat (limited to 'printutils')
-rw-r--r-- | printutils/Config.in | 15 | ||||
-rw-r--r-- | printutils/Kbuild | 8 | ||||
-rw-r--r-- | printutils/lpq.c | 108 | ||||
-rw-r--r-- | printutils/lpr.c | 200 | ||||
-rw-r--r-- | printutils/lpr.h | 17 | ||||
-rw-r--r-- | printutils/parse_prt.c | 27 |
6 files changed, 375 insertions, 0 deletions
diff --git a/printutils/Config.in b/printutils/Config.in new file mode 100644 index 000000000..b53b9e7fa --- /dev/null +++ b/printutils/Config.in | |||
@@ -0,0 +1,15 @@ | |||
1 | menu "print support" | ||
2 | |||
3 | config LPR | ||
4 | bool "lpr" | ||
5 | default n | ||
6 | help | ||
7 | lpr sends files (or standard input) to a print spooling daemon. | ||
8 | |||
9 | config LPQ | ||
10 | bool "lpq" | ||
11 | default n | ||
12 | help | ||
13 | lpq is a print spool queue examination and manipulation program. | ||
14 | |||
15 | endmenu | ||
diff --git a/printutils/Kbuild b/printutils/Kbuild new file mode 100644 index 000000000..f32272334 --- /dev/null +++ b/printutils/Kbuild | |||
@@ -0,0 +1,8 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
4 | |||
5 | lib-y := | ||
6 | |||
7 | lib-$(CONFIG_LPR) += lpr.o parse_prt.o | ||
8 | lib-$(CONFIG_LPQ) += lpq.o parse_prt.o | ||
diff --git a/printutils/lpq.c b/printutils/lpq.c new file mode 100644 index 000000000..ce9a10cb3 --- /dev/null +++ b/printutils/lpq.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 2008 Walter Harms (WHarms@bfs.de) | ||
4 | * | ||
5 | * Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | */ | ||
7 | #include "libbb.h" | ||
8 | #include "lpr.h" | ||
9 | |||
10 | /* | ||
11 | this is a *very* resticted form of lpq | ||
12 | since we do not read /etc/printcap (and never will) | ||
13 | we can only do things rfc1179 allows: | ||
14 | - show print jobs for a given queue long/short form | ||
15 | - remove a job from a given queue | ||
16 | |||
17 | -P <queue> | ||
18 | -s short | ||
19 | -d delete job | ||
20 | -f force any waiting job to be printed | ||
21 | */ | ||
22 | enum { | ||
23 | LPQ_SHORT = 1 << 0, | ||
24 | LPQ_DELETE = 1 << 1, | ||
25 | LPQ_FORCE = 1 << 2, | ||
26 | LPQ_P = 1 << 3, | ||
27 | LPQ_t = 1 << 4, | ||
28 | }; | ||
29 | |||
30 | /* | ||
31 | print everthing that comes | ||
32 | */ | ||
33 | static void get_answer(int sockfd) | ||
34 | { | ||
35 | char buf[80]; | ||
36 | int n; | ||
37 | |||
38 | buf[0] = '\n'; | ||
39 | while (1) { | ||
40 | n = safe_read(sockfd, buf, sizeof(buf)); | ||
41 | if (n <= 0) | ||
42 | break; | ||
43 | full_write(STDOUT_FILENO, buf, n); | ||
44 | buf[0] = buf[n-1]; /* last written char */ | ||
45 | } | ||
46 | |||
47 | /* Don't leave last output line unterminated */ | ||
48 | if (buf[0] != '\n') | ||
49 | full_write(STDOUT_FILENO, "\n", 1); | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | is this too simple ? | ||
54 | should we support more ENV ? | ||
55 | PRINTER, LPDEST, NPRINTER, NGPRINTER | ||
56 | */ | ||
57 | int lpq_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | ||
58 | int lpq_main(int argc, char *argv[]) | ||
59 | { | ||
60 | struct netprint netprint; | ||
61 | const char *netopt; | ||
62 | const char *delopt = "0"; | ||
63 | int sockfd = sockfd; /* for compiler */ | ||
64 | unsigned opt; | ||
65 | int delay; /* delay in [s] */ | ||
66 | |||
67 | netopt = NULL; | ||
68 | opt = getopt32(argv, "sdfP:t:", &netopt, &delopt); | ||
69 | argv += optind; | ||
70 | delay = xatoi_u(delopt); | ||
71 | parse_prt(netopt, &netprint); | ||
72 | |||
73 | /* do connect */ | ||
74 | if (opt & (LPQ_FORCE|LPQ_DELETE)) | ||
75 | sockfd = xconnect_stream(netprint.lsa); | ||
76 | |||
77 | /* force printing of every job still in queue */ | ||
78 | if (opt & LPQ_FORCE) { | ||
79 | fdprintf(sockfd, "\x1" "%s", netprint.queue); | ||
80 | get_answer(sockfd); | ||
81 | return EXIT_SUCCESS; | ||
82 | } | ||
83 | |||
84 | /* delete job (better a list of jobs). username is now LOGNAME */ | ||
85 | if (opt & LPQ_DELETE) { | ||
86 | while (*argv) { | ||
87 | fdprintf(sockfd, "\x5" "%s %s %s", | ||
88 | netprint.queue, | ||
89 | getenv("LOGNAME"), /* FIXME - may be NULL? */ | ||
90 | *argv); | ||
91 | get_answer(sockfd); | ||
92 | argv++; | ||
93 | } | ||
94 | return EXIT_SUCCESS; | ||
95 | } | ||
96 | |||
97 | do { | ||
98 | sockfd = xconnect_stream(netprint.lsa); | ||
99 | fdprintf(sockfd, "%c%s\n", (opt & LPQ_SHORT) ? 3 : 4, | ||
100 | netprint.queue); | ||
101 | |||
102 | get_answer(sockfd); | ||
103 | close(sockfd); | ||
104 | sleep(delay); | ||
105 | } while (delay != 0); | ||
106 | |||
107 | return EXIT_SUCCESS; | ||
108 | } | ||
diff --git a/printutils/lpr.c b/printutils/lpr.c new file mode 100644 index 000000000..b8c77bfc3 --- /dev/null +++ b/printutils/lpr.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 2008 Walter Harms (WHarms@bfs.de) | ||
4 | * | ||
5 | * Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | */ | ||
7 | #include "libbb.h" | ||
8 | #include "lpr.h" | ||
9 | |||
10 | static char *mygethostname31(void) | ||
11 | { | ||
12 | char *s = xzalloc(32); | ||
13 | if (gethostname(s, 31) < 0) | ||
14 | bb_perror_msg_and_die("gethostname"); | ||
15 | /* gethostname() does not guarantee NUL-termination. xzalloc does. */ | ||
16 | return s; | ||
17 | } | ||
18 | |||
19 | static int xmkstemp(char *temp_name) | ||
20 | { | ||
21 | int fd; | ||
22 | |||
23 | fd = mkstemp(temp_name); | ||
24 | if (fd < 0) | ||
25 | bb_perror_msg_and_die("mkstemp"); | ||
26 | return fd; | ||
27 | } | ||
28 | |||
29 | /* lpd server sends NUL byte on success. | ||
30 | * Else read the errormessage and exit. | ||
31 | */ | ||
32 | static void get_response(int server_sock, const char *emsg) | ||
33 | { | ||
34 | char buf = '\0'; | ||
35 | |||
36 | safe_read(server_sock, &buf, 1); | ||
37 | if (buf != '\0') { | ||
38 | bb_error_msg("%s. Server said:", emsg); | ||
39 | fputc(buf, stderr); | ||
40 | logmode = 0; /* no errors from bb_copyfd_eof() */ | ||
41 | bb_copyfd_eof(server_sock, STDERR_FILENO); | ||
42 | xfunc_die(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | int lpr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | ||
47 | int lpr_main(int argc, char *argv[]) | ||
48 | { | ||
49 | struct netprint netprint; | ||
50 | char temp_name[] = "/tmp/lprXXXXXX"; /* for mkstemp */ | ||
51 | char *strings[5]; | ||
52 | const char *netopt; | ||
53 | const char *jobtitle; | ||
54 | const char *hostname; | ||
55 | const char *jobclass; | ||
56 | char *logname; | ||
57 | int pid1000; | ||
58 | int server_sock, tmp_fd; | ||
59 | unsigned opt; | ||
60 | enum { | ||
61 | VERBOSE = 1 << 0, | ||
62 | USE_HEADER = 1 << 1, /* -h banner or header for this job */ | ||
63 | USE_MAIL = 1 << 2, /* send mail to user@hostname */ | ||
64 | OPT_U = 1 << 3, /* -U <username> */ | ||
65 | OPT_J = 1 << 4, /* -J <title> is the jobtitle for the banner page */ | ||
66 | OPT_C = 1 << 5, /* -C <class> job classification */ | ||
67 | OPT_P = 1 << 6, /* -P <queue[@host[:port]]> */ | ||
68 | /* if no -P is given use $PRINTER, then "lp@localhost:515" */ | ||
69 | }; | ||
70 | |||
71 | /* Set defaults, parse options */ | ||
72 | hostname = mygethostname31(); | ||
73 | netopt = NULL; | ||
74 | logname = getenv("LOGNAME"); | ||
75 | if (logname == NULL) | ||
76 | logname = (char*)"user"; /* TODO: getpwuid(getuid())->pw_name? */ | ||
77 | opt = getopt32(argv, "VhmU:J:C:P:", | ||
78 | &logname, &jobtitle, &jobclass, &netopt); | ||
79 | argv += optind; | ||
80 | parse_prt(netopt, &netprint); | ||
81 | logname = xstrndup(logname, 31); | ||
82 | |||
83 | /* For stdin we need to save it to a tempfile, | ||
84 | * otherwise we can't know the size. */ | ||
85 | tmp_fd = -1; | ||
86 | if (!*argv) { | ||
87 | if (jobtitle == NULL) | ||
88 | jobtitle = (char *) bb_msg_standard_input; | ||
89 | |||
90 | tmp_fd = xmkstemp(temp_name); | ||
91 | if (bb_copyfd_eof(STDIN_FILENO, tmp_fd) < 0) | ||
92 | goto del_temp_file; | ||
93 | /* TODO: we actually can have deferred write errors... */ | ||
94 | close(tmp_fd); | ||
95 | argv--; /* back off from NULL */ | ||
96 | *argv = temp_name; | ||
97 | } | ||
98 | |||
99 | if (opt & VERBOSE) | ||
100 | puts("connect to server"); | ||
101 | server_sock = xconnect_stream(netprint.lsa); | ||
102 | |||
103 | /* "Receive a printer job" command */ | ||
104 | fdprintf(server_sock, "\x2" "%s\n", netprint.queue); | ||
105 | get_response(server_sock, "set queue failed"); | ||
106 | |||
107 | pid1000 = getpid() % 1000; | ||
108 | while (*argv) { | ||
109 | char dfa_name[sizeof("dfAnnn") + 32]; | ||
110 | struct stat st; | ||
111 | char **sptr; | ||
112 | unsigned size; | ||
113 | int fd; | ||
114 | |||
115 | fd = xopen(*argv, O_RDONLY); | ||
116 | |||
117 | /* "The name ... should start with ASCII "dfA", | ||
118 | * followed by a three digit job number, followed | ||
119 | * by the host name which has constructed the file." */ | ||
120 | snprintf(dfa_name, sizeof(dfa_name), | ||
121 | "dfA%03u%s", pid1000, hostname); | ||
122 | pid1000 = (pid1000 + 1) % 1000; | ||
123 | |||
124 | /* | ||
125 | * Generate control file contents | ||
126 | */ | ||
127 | /* H HOST, P USER, l DATA_FILE_NAME, J JOBNAME */ | ||
128 | strings[0] = xasprintf("H%.32s\n" "P%.32s\n" "l%.32s\n" | ||
129 | "J%.99s\n", | ||
130 | hostname, logname, dfa_name, | ||
131 | !(opt & OPT_J) ? *argv : jobtitle); | ||
132 | sptr = &strings[1]; | ||
133 | /* C CLASS - printed on banner page (if L cmd is also given) */ | ||
134 | if (opt & OPT_J) /* [1] */ | ||
135 | *sptr++ = xasprintf("C%.32s\n", jobclass); | ||
136 | /* M WHOM_TO_MAIL */ | ||
137 | if (opt & USE_MAIL) /* [2] */ | ||
138 | *sptr++ = xasprintf("M%.32s\n", logname); | ||
139 | /* H USER - print banner page, with given user's name */ | ||
140 | if (opt & USE_HEADER) /* [3] */ | ||
141 | *sptr++ = xasprintf("L%.32s\n", logname); | ||
142 | *sptr = NULL; /* [4] max */ | ||
143 | |||
144 | /* RFC 1179: "LPR servers MUST be able | ||
145 | * to receive the control file subcommand first | ||
146 | * and SHOULD be able to receive the data file | ||
147 | * subcommand first". | ||
148 | * Ok, we'll send control file first. */ | ||
149 | size = 0; | ||
150 | sptr = strings; | ||
151 | while (*sptr) | ||
152 | size += strlen(*sptr++); | ||
153 | if (opt & VERBOSE) | ||
154 | puts("send control file"); | ||
155 | /* 2: "Receive control file" subcommand */ | ||
156 | fdprintf(server_sock, "\x2" "%u c%s\n", size, dfa_name + 1); | ||
157 | sptr = strings; | ||
158 | while (*sptr) { | ||
159 | xwrite(server_sock, *sptr, strlen(*sptr)); | ||
160 | free(*sptr); | ||
161 | sptr++; | ||
162 | } | ||
163 | free(strings); | ||
164 | /* "Once all of the contents have | ||
165 | * been delivered, an octet of zero bits is sent as | ||
166 | * an indication that the file being sent is complete. | ||
167 | * A second level of acknowledgement processing | ||
168 | * must occur at this point." */ | ||
169 | xwrite(server_sock, "", 1); | ||
170 | get_response(server_sock, "send control file failed"); | ||
171 | |||
172 | /* Sending data */ | ||
173 | st.st_size = 0; /* paranoia */ | ||
174 | fstat(fd, &st); | ||
175 | if (opt & VERBOSE) | ||
176 | puts("send data file"); | ||
177 | /* 3: "Receive data file" subcommand */ | ||
178 | fdprintf(server_sock, "\x3" "%"OFF_FMT"u %s\n", st.st_size, dfa_name); | ||
179 | /* TODO: if file shrank and we wrote less than st.st_size, | ||
180 | * pad output with NUL bytes? Otherwise server won't know | ||
181 | * that we are done. */ | ||
182 | if (bb_copyfd_size(fd, server_sock, st.st_size) < 0) | ||
183 | xfunc_die(); | ||
184 | close(fd); | ||
185 | xwrite(server_sock, "", 1); | ||
186 | get_response(server_sock, "send file failed"); | ||
187 | |||
188 | argv++; | ||
189 | } | ||
190 | |||
191 | if (ENABLE_FEATURE_CLEAN_UP) | ||
192 | close(server_sock); | ||
193 | |||
194 | if (tmp_fd >= 0) { | ||
195 | del_temp_file: | ||
196 | unlink(temp_name); | ||
197 | } | ||
198 | |||
199 | return 0; | ||
200 | } | ||
diff --git a/printutils/lpr.h b/printutils/lpr.h new file mode 100644 index 000000000..8898b982f --- /dev/null +++ b/printutils/lpr.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 2008 Walter Harms (WHarms@bfs.de) | ||
4 | * | ||
5 | * Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | */ | ||
7 | #ifndef _LPR_H_ | ||
8 | #define _LPR_H_ | ||
9 | |||
10 | struct netprint { | ||
11 | char *queue; | ||
12 | char *server; | ||
13 | struct len_and_sockaddr *lsa; | ||
14 | }; | ||
15 | |||
16 | void parse_prt(const char *buf, struct netprint *netprint); | ||
17 | #endif | ||
diff --git a/printutils/parse_prt.c b/printutils/parse_prt.c new file mode 100644 index 000000000..2de0a9215 --- /dev/null +++ b/printutils/parse_prt.c | |||
@@ -0,0 +1,27 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 2008 Walter Harms (WHarms@bfs.de) | ||
4 | * | ||
5 | * Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | */ | ||
7 | #include "libbb.h" | ||
8 | #include "lpr.h" | ||
9 | |||
10 | void parse_prt(const char *buf, struct netprint *netprint) | ||
11 | { | ||
12 | const char *p; | ||
13 | |||
14 | if (!buf) { | ||
15 | buf = getenv("PRINTER"); | ||
16 | if (!buf) | ||
17 | buf = "lp"; /* "...@localhost:515" is implied */ | ||
18 | } | ||
19 | p = strchrnul(buf, '@'); | ||
20 | netprint->queue = xstrndup(buf, p - buf); | ||
21 | if (!*p) /* just queue? example: "lpq -Pcopie" */ | ||
22 | p = "localhost"; | ||
23 | netprint->server = xstrdup(p); | ||
24 | |||
25 | netprint->lsa = xhost2sockaddr(netprint->server, | ||
26 | bb_lookup_port(NULL, "tcp", 515)); | ||
27 | } | ||