1 /*
2  * Copyright (C) 1992-1997 Michael K. Johnson, johnsonm@redhat.com
3  *
4  * This file is licensed under the terms of the GNU General Public
5  * License, version 2, or any later version.  See file COPYING for
6  * information on distribution conditions.
7  */
8 
9 /*
10  * $Log: tunelp.c,v $
11  * Revision 1.9  1998/06/08 19:37:11  janl
12  * Thus compiles tunelp with 2.1.103 kernels
13  *
14  * Revision 1.8  1997/07/06 00:14:06  aebr
15  * Fixes to silence -Wall.
16  *
17  * Revision 1.7  1997/06/20 16:10:38  janl
18  * tunelp refreshed from authors archive.
19  *
20  * Revision 1.9  1997/06/20 12:56:43  johnsonm
21  * Finished fixing license terms.
22  *
23  * Revision 1.8  1997/06/20 12:34:59  johnsonm
24  * Fixed copyright and license.
25  *
26  * Revision 1.7  1995/03/29 11:16:23  johnsonm
27  * TYPO fixed...
28  *
29  * Revision 1.6  1995/03/29  11:12:15  johnsonm
30  * Added third argument to ioctl needed with new kernels
31  *
32  * Revision 1.5  1995/01/13  10:33:43  johnsonm
33  * Chris's changes for new ioctl numbers and backwards compatibility
34  * and the reset ioctl.
35  *
36  * Revision 1.4  1995/01/03  17:42:14  johnsonm
37  * -s isn't supposed to take an argument; removed : after s in getopt...
38  *
39  * Revision 1.3  1995/01/03  07:36:49  johnsonm
40  * Fixed typo
41  *
42  * Revision 1.2  1995/01/03  07:33:44  johnsonm
43  * revisions for lp driver updates in Linux 1.1.76
44  *
45  * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
46  * - added Native Language Support
47  *
48  * 1999-05-07 Merged LPTRUSTIRQ patch by Andrea Arcangeli (1998/11/29), aeb
49  *
50  */
51 
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <getopt.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <sys/ioctl.h>
59 #include <sys/stat.h>
60 #include <sys/types.h>
61 #include <unistd.h>
62 
63 #include "lp.h"
64 #include "nls.h"
65 #include "xalloc.h"
66 #include "closestream.h"
67 
68 #define EXIT_BAD_VALUE	3
69 #define EXIT_LP_IO_ERR	4
70 
71 struct command {
72 	long op;
73 	long val;
74 	struct command *next;
75 };
76 
print_usage(FILE * out)77 static void __attribute__((__noreturn__)) print_usage(FILE *out)
78 {
79 	fputs(USAGE_HEADER, out);
80 	fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
81 
82 	fputs(USAGE_OPTIONS, out);
83 	fputs(_(" -i, --irq <num>              specify parallel port irq\n"), out);
84 	fputs(_(" -t, --time <ms>              driver wait time in milliseconds\n"), out);
85 	fputs(_(" -c, --chars <num>            number of output characters before sleep\n"), out);
86 	fputs(_(" -w, --wait <us>              strobe wait in micro seconds\n"), out);
87 	/* TRANSLATORS: do not translate <on|off> arguments. The
88 	   argument reader does not recognize locale, unless `on' is
89 	   exactly that very same string. */
90 	fputs(_(" -a, --abort <on|off>         abort on error\n"), out);
91 	fputs(_(" -o, --check-status <on|off>  check printer status before printing\n"), out);
92 	fputs(_(" -C, --careful <on|off>       extra checking to status check\n"), out);
93 	fputs(_(" -s, --status                 query printer status\n"), out);
94 	fputs(_(" -T, --trust-irq <on|off>     make driver to trust irq\n"), out);
95 	fputs(_(" -r, --reset                  reset the port\n"), out);
96 	fputs(_(" -q, --print-irq <on|off>     display current irq setting\n"), out);
97 	fputs(USAGE_SEPARATOR, out);
98 	fputs(USAGE_HELP, out);
99 	fputs(USAGE_VERSION, out);
100 	fprintf(out, USAGE_MAN_TAIL("tunelp(8)"));
101 
102 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
103 }
104 
get_val(char * val)105 static long get_val(char *val)
106 {
107 	long ret;
108 	if (!(sscanf(val, "%ld", &ret) == 1))
109 		errx(EXIT_BAD_VALUE, _("bad value"));
110 	return ret;
111 }
112 
get_onoff(char * val)113 static long get_onoff(char *val)
114 {
115 	if (!strncasecmp("on", val, 2))
116 		return 1;
117 	return 0;
118 }
119 
main(int argc,char ** argv)120 int main(int argc, char **argv)
121 {
122 	int c, fd, irq, status, show_irq, offset = 0, retval;
123 	char *filename;
124 	struct stat statbuf;
125 	struct command *cmds, *cmdst;
126 	static const struct option longopts[] = {
127 		{"irq", required_argument, NULL, 'i'},
128 		{"time", required_argument, NULL, 't'},
129 		{"chars", required_argument, NULL, 'c'},
130 		{"wait", required_argument, NULL, 'w'},
131 		{"abort", required_argument, NULL, 'a'},
132 		{"check-status", required_argument, NULL, 'o'},
133 		{"careful", required_argument, NULL, 'C'},
134 		{"status", no_argument, NULL, 's'},
135 		{"trust-irq", required_argument, NULL, 'T'},
136 		{"reset", no_argument, NULL, 'r'},
137 		{"print-irq", required_argument, NULL, 'q'},
138 		{"version", no_argument, NULL, 'V'},
139 		{"help", no_argument, NULL, 'h'},
140 		{NULL, 0, NULL, 0}
141 	};
142 
143 	setlocale(LC_ALL, "");
144 	bindtextdomain(PACKAGE, LOCALEDIR);
145 	textdomain(PACKAGE);
146 	atexit(close_stdout);
147 
148 	if (argc < 2)
149 		print_usage(stderr);
150 
151 	cmdst = cmds = xmalloc(sizeof(struct command));
152 	cmds->next = 0;
153 
154 	show_irq = 1;
155 	while ((c = getopt_long(argc, argv, "t:c:w:a:i:ho:C:sq:rT:vV", longopts, NULL)) != -1) {
156 		switch (c) {
157 		case 'h':
158 			print_usage(stdout);
159 			break;
160 		case 'i':
161 			cmds->op = LPSETIRQ;
162 			cmds->val = get_val(optarg);
163 			cmds->next = xmalloc(sizeof(struct command));
164 			cmds = cmds->next;
165 			cmds->next = 0;
166 			break;
167 		case 't':
168 			cmds->op = LPTIME;
169 			cmds->val = get_val(optarg);
170 			cmds->next = xmalloc(sizeof(struct command));
171 			cmds = cmds->next;
172 			cmds->next = 0;
173 			break;
174 		case 'c':
175 			cmds->op = LPCHAR;
176 			cmds->val = get_val(optarg);
177 			cmds->next = xmalloc(sizeof(struct command));
178 			cmds = cmds->next;
179 			cmds->next = 0;
180 			break;
181 		case 'w':
182 			cmds->op = LPWAIT;
183 			cmds->val = get_val(optarg);
184 			cmds->next = xmalloc(sizeof(struct command));
185 			cmds = cmds->next;
186 			cmds->next = 0;
187 			break;
188 		case 'a':
189 			cmds->op = LPABORT;
190 			cmds->val = get_onoff(optarg);
191 			cmds->next = xmalloc(sizeof(struct command));
192 			cmds = cmds->next;
193 			cmds->next = 0;
194 			break;
195 		case 'q':
196 			if (get_onoff(optarg)) {
197 				show_irq = 1;
198 			} else {
199 				show_irq = 0;
200 			}
201 			break;
202 #ifdef LPGETSTATUS
203 		case 'o':
204 			cmds->op = LPABORTOPEN;
205 			cmds->val = get_onoff(optarg);
206 			cmds->next = xmalloc(sizeof(struct command));
207 			cmds = cmds->next;
208 			cmds->next = 0;
209 			break;
210 		case 'C':
211 			cmds->op = LPCAREFUL;
212 			cmds->val = get_onoff(optarg);
213 			cmds->next = xmalloc(sizeof(struct command));
214 			cmds = cmds->next;
215 			cmds->next = 0;
216 			break;
217 		case 's':
218 			show_irq = 0;
219 			cmds->op = LPGETSTATUS;
220 			cmds->val = 0;
221 			cmds->next = xmalloc(sizeof(struct command));
222 			cmds = cmds->next;
223 			cmds->next = 0;
224 			break;
225 #endif
226 #ifdef LPRESET
227 		case 'r':
228 			cmds->op = LPRESET;
229 			cmds->val = 0;
230 			cmds->next = xmalloc(sizeof(struct command));
231 			cmds = cmds->next;
232 			cmds->next = 0;
233 			break;
234 #endif
235 #ifdef LPTRUSTIRQ
236 		case 'T':
237 			/* Note: this will do the wrong thing on
238 			 * 2.0.36 when compiled under 2.2.x
239 			 */
240 			cmds->op = LPTRUSTIRQ;
241 			cmds->val = get_onoff(optarg);
242 			cmds->next = xmalloc(sizeof(struct command));
243 			cmds = cmds->next;
244 			cmds->next = 0;
245 			break;
246 #endif
247 		case 'v':
248 		case 'V':
249 			printf(_("%s from %s\n"),
250 			       program_invocation_short_name, PACKAGE_STRING);
251 			return EXIT_SUCCESS;
252 		default:
253 			print_usage(stderr);
254 		}
255 	}
256 
257 	if (optind != argc - 1)
258 		print_usage(stderr);
259 
260 	filename = xstrdup(argv[optind]);
261 	fd = open(filename, O_WRONLY | O_NONBLOCK, 0);
262 	/* Need to open O_NONBLOCK in case ABORTOPEN is already set
263 	 * and printer is off or off-line or in an error condition.
264 	 * Otherwise we would abort...
265          */
266 	if (fd < 0)
267 		err(EXIT_FAILURE, "%s", filename);
268 
269 	if (fstat(fd, &statbuf))
270 		err(EXIT_FAILURE, "%s: stat() failed", filename);
271 
272 	if (!S_ISCHR(statbuf.st_mode)) {
273 		warnx(_("%s not an lp device"), filename);
274 		print_usage(stderr);
275 	}
276 	/* Allow for binaries compiled under a new kernel to work on
277 	 * the old ones The irq argument to ioctl isn't touched by
278 	 * the old kernels, but we don't want to cause the kernel to
279 	 * complain if we are using a new kernel
280 	 */
281 	if (LPGETIRQ >= 0x0600 && ioctl(fd, LPGETIRQ, &irq) < 0
282 	    && errno == EINVAL)
283 	        /* We don't understand the new ioctls */
284 		offset = 0x0600;
285 
286 	cmds = cmdst;
287 	while (cmds->next) {
288 #ifdef LPGETSTATUS
289 		if (cmds->op == LPGETSTATUS) {
290 			status = 0xdeadbeef;
291 			retval = ioctl(fd, LPGETSTATUS - offset, &status);
292 			if (retval < 0)
293 				warnx(_("LPGETSTATUS error"));
294 			else {
295 				if (status == (int)0xdeadbeef)
296 					/* a few 1.1.7x kernels will do this */
297 					status = retval;
298 				printf(_("%s status is %d"), filename, status);
299 				if (!(status & LP_PBUSY))
300 					printf(_(", busy"));
301 				if (!(status & LP_PACK))
302 					printf(_(", ready"));
303 				if ((status & LP_POUTPA))
304 					printf(_(", out of paper"));
305 				if ((status & LP_PSELECD))
306 					printf(_(", on-line"));
307 				if (!(status & LP_PERRORP))
308 					printf(_(", error"));
309 				printf("\n");
310 			}
311 		} else
312 #endif /* LPGETSTATUS */
313 		if (ioctl(fd, cmds->op - offset, cmds->val) < 0)
314 			warn(_("ioctl failed"));
315 		cmdst = cmds;
316 		cmds = cmds->next;
317 		free(cmdst);
318 	}
319 
320 	if (show_irq) {
321 		irq = 0xdeadbeef;
322 		retval = ioctl(fd, LPGETIRQ - offset, &irq);
323 		if (retval == -1)
324 			err(EXIT_LP_IO_ERR, _("LPGETIRQ error"));
325 		if (irq == (int)0xdeadbeef)
326 		        /* up to 1.1.77 will do this */
327 			irq = retval;
328 		if (irq)
329 			printf(_("%s using IRQ %d\n"), filename, irq);
330 		else
331 			printf(_("%s using polling\n"), filename);
332 	}
333 	free(filename);
334 	close(fd);
335 
336 	return EXIT_SUCCESS;
337 }
338