1/* $OpenBSD: lp_stty.c,v 1.1.1.1 2018/04/27 16:14:36 eric Exp $ */
2
3/*
4 * Adapted from the following files in src/usr.sbin/lpr/lpd:
5 *
6 * extern.h,v 1.9 2015/09/29 02:37:29
7 * key.c,v 1.8 2015/01/16 06:40:18
8 * modes.c,v 1.8 2015/01/16 06:40:18
9 * printjob.c,v 1.58 2016/11/22 16:03:57
10 */
11
12static const char *printer;
13
14/*-
15 *
16 * Copyright (c) 1989, 1993
17 *	The Regents of the University of California.  All rights reserved.
18 * Copyright (c) 1991, 1993, 1994
19 *	The Regents of the University of California.  All rights reserved.
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 *    notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 *    notice, this list of conditions and the following disclaimer in the
28 *    documentation and/or other materials provided with the distribution.
29 * 3. Neither the name of the University nor the names of its contributors
30 *    may be used to endorse or promote products derived from this software
31 *    without specific prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 */
45
46#include <sys/types.h>
47#include <sys/ioctl.h>
48
49#include <errno.h>
50#include <signal.h>
51#include <stdlib.h>
52#include <stdio.h>
53#include <string.h>
54#include <dirent.h>
55#include <limits.h>
56#include <termios.h>
57
58#include "lp.h"
59#include "log.h"
60
61/*
62 * from extern.h
63 */
64
65struct info {
66	int fd;					/* file descriptor */
67	int ldisc;				/* line discipline */
68	int off;				/* turn off */
69	int set;				/* need set */
70	int wset;				/* need window set */
71	char *arg;				/* argument */
72	struct termios t;			/* terminal info */
73	struct winsize win;			/* window info */
74};
75
76
77/*
78 * from key.c
79 */
80
81static int  c_key(const void *, const void *);
82static void f_cbreak(struct info *);
83static void f_columns(struct info *);
84static void f_dec(struct info *);
85static void f_extproc(struct info *);
86static void f_ispeed(struct info *);
87static void f_nl(struct info *);
88static void f_ospeed(struct info *);
89static void f_raw(struct info *);
90static void f_rows(struct info *);
91static void f_sane(struct info *);
92static void f_tty(struct info *);
93
94static struct key {
95	char *name;				/* name */
96	void (*f)(struct info *);		/* function */
97#define	F_NEEDARG	0x01			/* needs an argument */
98#define	F_OFFOK		0x02			/* can turn off */
99	int flags;
100} const keys[] = {
101	{ "cbreak",	f_cbreak,	F_OFFOK },
102	{ "cols",	f_columns,	F_NEEDARG },
103	{ "columns",	f_columns,	F_NEEDARG },
104	{ "cooked", 	f_sane,		0 },
105	{ "dec",	f_dec,		0 },
106	{ "extproc",	f_extproc,	F_OFFOK },
107	{ "ispeed",	f_ispeed,	F_NEEDARG },
108	{ "new",	f_tty,		0 },
109	{ "nl",		f_nl,		F_OFFOK },
110	{ "old",	f_tty,		0 },
111	{ "ospeed",	f_ospeed,	F_NEEDARG },
112	{ "raw",	f_raw,		F_OFFOK },
113	{ "rows",	f_rows,		F_NEEDARG },
114	{ "sane",	f_sane,		0 },
115	{ "tty",	f_tty,		0 },
116};
117
118static int
119c_key(const void *a, const void *b)
120{
121
122        return (strcmp(((const struct key *)a)->name,
123	    ((const struct key *)b)->name));
124}
125
126static int
127ksearch(char ***argvp, struct info *ip)
128{
129	char *name;
130	struct key *kp, tmp;
131
132	name = **argvp;
133	if (*name == '-') {
134		ip->off = 1;
135		++name;
136	} else
137		ip->off = 0;
138
139	tmp.name = name;
140	if (!(kp = (struct key *)bsearch(&tmp, keys,
141	    sizeof(keys)/sizeof(struct key), sizeof(struct key), c_key)))
142		return (0);
143	if (!(kp->flags & F_OFFOK) && ip->off) {
144		log_warnx("%s: illegal option: %s", printer, name);
145		return (1);
146	}
147	if (kp->flags & F_NEEDARG && !(ip->arg = *++*argvp)) {
148		log_warnx("%s: option requires an argument: %s", printer, name);
149		return (1);
150	}
151	kp->f(ip);
152	return (1);
153}
154
155static void
156f_cbreak(struct info *ip)
157{
158
159	if (ip->off)
160		f_sane(ip);
161	else {
162		ip->t.c_iflag |= BRKINT|IXON|IMAXBEL;
163		ip->t.c_oflag |= OPOST;
164		ip->t.c_lflag |= ISIG|IEXTEN;
165		ip->t.c_lflag &= ~ICANON;
166		ip->set = 1;
167	}
168}
169
170static void
171f_columns(struct info *ip)
172{
173
174	ip->win.ws_col = atoi(ip->arg);
175	ip->wset = 1;
176}
177
178static void
179f_dec(struct info *ip)
180{
181
182	ip->t.c_cc[VERASE] = (u_char)0177;
183	ip->t.c_cc[VKILL] = CTRL('u');
184	ip->t.c_cc[VINTR] = CTRL('c');
185	ip->t.c_lflag &= ~ECHOPRT;
186	ip->t.c_lflag |= ECHOE|ECHOKE|ECHOCTL;
187	ip->t.c_iflag &= ~IXANY;
188	ip->set = 1;
189}
190
191static void
192f_extproc(struct info *ip)
193{
194
195	if (ip->set) {
196		int tmp = 1;
197		(void)ioctl(ip->fd, TIOCEXT, &tmp);
198	} else {
199		int tmp = 0;
200		(void)ioctl(ip->fd, TIOCEXT, &tmp);
201	}
202}
203
204static void
205f_ispeed(struct info *ip)
206{
207
208	cfsetispeed(&ip->t, atoi(ip->arg));
209	ip->set = 1;
210}
211
212static void
213f_nl(struct info *ip)
214{
215
216	if (ip->off) {
217		ip->t.c_iflag |= ICRNL;
218		ip->t.c_oflag |= ONLCR;
219	} else {
220		ip->t.c_iflag &= ~ICRNL;
221		ip->t.c_oflag &= ~ONLCR;
222	}
223	ip->set = 1;
224}
225
226static void
227f_ospeed(struct info *ip)
228{
229
230	cfsetospeed(&ip->t, atoi(ip->arg));
231	ip->set = 1;
232}
233
234static void
235f_raw(struct info *ip)
236{
237
238	if (ip->off)
239		f_sane(ip);
240	else {
241		cfmakeraw(&ip->t);
242		ip->t.c_cflag &= ~(CSIZE|PARENB);
243		ip->t.c_cflag |= CS8;
244		ip->set = 1;
245	}
246}
247
248static void
249f_rows(struct info *ip)
250{
251
252	ip->win.ws_row = atoi(ip->arg);
253	ip->wset = 1;
254}
255
256static void
257f_sane(struct info *ip)
258{
259
260	ip->t.c_cflag = TTYDEF_CFLAG | (ip->t.c_cflag & (CLOCAL|CRTSCTS));
261	ip->t.c_iflag = TTYDEF_IFLAG;
262	ip->t.c_iflag |= ICRNL;
263	/* preserve user-preference flags in lflag */
264#define	LKEEP	(ECHOKE|ECHOE|ECHOK|ECHOPRT|ECHOCTL|ALTWERASE|TOSTOP|NOFLSH)
265	ip->t.c_lflag = TTYDEF_LFLAG | (ip->t.c_lflag & LKEEP);
266	ip->t.c_oflag = TTYDEF_OFLAG;
267	ip->set = 1;
268}
269
270static void
271f_tty(struct info *ip)
272{
273	int tmp;
274
275	tmp = TTYDISC;
276	if (ioctl(0, TIOCSETD, &tmp) == -1)
277		log_warn("%s: ioctl(TIOCSETD)", printer);
278}
279
280/*
281 * from key.c
282 */
283
284struct modes {
285	char *name;
286	long set;
287	long unset;
288};
289
290/*
291 * The code in optlist() depends on minus options following regular
292 * options, i.e. "foo" must immediately precede "-foo".
293 */
294const struct modes cmodes[] = {
295	{ "cs5",	CS5, CSIZE },
296	{ "cs6",	CS6, CSIZE },
297	{ "cs7",	CS7, CSIZE },
298	{ "cs8",	CS8, CSIZE },
299	{ "cstopb",	CSTOPB, 0 },
300	{ "-cstopb",	0, CSTOPB },
301	{ "cread",	CREAD, 0 },
302	{ "-cread",	0, CREAD },
303	{ "parenb",	PARENB, 0 },
304	{ "-parenb",	0, PARENB },
305	{ "parodd",	PARODD, 0 },
306	{ "-parodd",	0, PARODD },
307	{ "parity",	PARENB | CS7, PARODD | CSIZE },
308	{ "-parity",	CS8, PARODD | PARENB | CSIZE },
309	{ "evenp",	PARENB | CS7, PARODD | CSIZE },
310	{ "-evenp",	CS8, PARODD | PARENB | CSIZE },
311	{ "oddp",	PARENB | CS7 | PARODD, CSIZE },
312	{ "-oddp",	CS8, PARODD | PARENB | CSIZE },
313	{ "pass8",	CS8, PARODD | PARENB | CSIZE },
314	{ "-pass8",	PARENB | CS7, PARODD | CSIZE },
315	{ "hupcl",	HUPCL, 0 },
316	{ "-hupcl",	0, HUPCL },
317	{ "hup",	HUPCL, 0 },
318	{ "-hup",	0, HUPCL },
319	{ "clocal",	CLOCAL, 0 },
320	{ "-clocal",	0, CLOCAL },
321	{ "crtscts",	CRTSCTS, 0 },
322	{ "-crtscts",	0, CRTSCTS },
323	{ "mdmbuf",	MDMBUF, 0 },
324	{ "-mdmbuf",	0, MDMBUF },
325	{ NULL },
326};
327
328const struct modes imodes[] = {
329	{ "ignbrk",	IGNBRK, 0 },
330	{ "-ignbrk",	0, IGNBRK },
331	{ "brkint",	BRKINT, 0 },
332	{ "-brkint",	0, BRKINT },
333	{ "ignpar",	IGNPAR, 0 },
334	{ "-ignpar",	0, IGNPAR },
335	{ "parmrk",	PARMRK, 0 },
336	{ "-parmrk",	0, PARMRK },
337	{ "inpck",	INPCK, 0 },
338	{ "-inpck",	0, INPCK },
339	{ "istrip",	ISTRIP, 0 },
340	{ "-istrip",	0, ISTRIP },
341	{ "inlcr",	INLCR, 0 },
342	{ "-inlcr",	0, INLCR },
343	{ "igncr",	IGNCR, 0 },
344	{ "-igncr",	0, IGNCR },
345	{ "icrnl",	ICRNL, 0 },
346	{ "-icrnl",	0, ICRNL },
347	{ "iuclc",	IUCLC, 0 },
348	{ "-iuclc",	0, IUCLC },
349	{ "ixon",	IXON, 0 },
350	{ "-ixon",	0, IXON },
351	{ "flow",	IXON, 0 },
352	{ "-flow",	0, IXON },
353	{ "ixoff",	IXOFF, 0 },
354	{ "-ixoff",	0, IXOFF },
355	{ "tandem",	IXOFF, 0 },
356	{ "-tandem",	0, IXOFF },
357	{ "ixany",	IXANY, 0 },
358	{ "-ixany",	0, IXANY },
359	{ "decctlq",	0, IXANY },
360	{ "-decctlq",	IXANY, 0 },
361	{ "imaxbel",	IMAXBEL, 0 },
362	{ "-imaxbel",	0, IMAXBEL },
363	{ NULL },
364};
365
366const struct modes lmodes[] = {
367	{ "echo",	ECHO, 0 },
368	{ "-echo",	0, ECHO },
369	{ "echoe",	ECHOE, 0 },
370	{ "-echoe",	0, ECHOE },
371	{ "crterase",	ECHOE, 0 },
372	{ "-crterase",	0, ECHOE },
373	{ "crtbs",	ECHOE, 0 },	/* crtbs not supported, close enough */
374	{ "-crtbs",	0, ECHOE },
375	{ "echok",	ECHOK, 0 },
376	{ "-echok",	0, ECHOK },
377	{ "echoke",	ECHOKE, 0 },
378	{ "-echoke",	0, ECHOKE },
379	{ "crtkill",	ECHOKE, 0 },
380	{ "-crtkill",	0, ECHOKE },
381	{ "altwerase",	ALTWERASE, 0 },
382	{ "-altwerase",	0, ALTWERASE },
383	{ "iexten",	IEXTEN, 0 },
384	{ "-iexten",	0, IEXTEN },
385	{ "echonl",	ECHONL, 0 },
386	{ "-echonl",	0, ECHONL },
387	{ "echoctl",	ECHOCTL, 0 },
388	{ "-echoctl",	0, ECHOCTL },
389	{ "ctlecho",	ECHOCTL, 0 },
390	{ "-ctlecho",	0, ECHOCTL },
391	{ "echoprt",	ECHOPRT, 0 },
392	{ "-echoprt",	0, ECHOPRT },
393	{ "prterase",	ECHOPRT, 0 },
394	{ "-prterase",	0, ECHOPRT },
395	{ "isig",	ISIG, 0 },
396	{ "-isig",	0, ISIG },
397	{ "icanon",	ICANON, 0 },
398	{ "-icanon",	0, ICANON },
399	{ "noflsh",	NOFLSH, 0 },
400	{ "-noflsh",	0, NOFLSH },
401	{ "tostop",	TOSTOP, 0 },
402	{ "-tostop",	0, TOSTOP },
403	{ "flusho",	FLUSHO, 0 },
404	{ "-flusho",	0, FLUSHO },
405	{ "pendin",	PENDIN, 0 },
406	{ "-pendin",	0, PENDIN },
407	{ "crt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
408	{ "-crt",	ECHOK, ECHOE|ECHOKE|ECHOCTL },
409	{ "newcrt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
410	{ "-newcrt",	ECHOK, ECHOE|ECHOKE|ECHOCTL },
411	{ "nokerninfo",	NOKERNINFO, 0 },
412	{ "-nokerninfo",0, NOKERNINFO },
413	{ "kerninfo",	0, NOKERNINFO },
414	{ "-kerninfo",	NOKERNINFO, 0 },
415	{ "xcase",	XCASE, 0 },
416	{ "-xcase",	0, XCASE },
417	{ NULL },
418};
419
420const struct modes omodes[] = {
421	{ "opost",	OPOST, 0 },
422	{ "-opost",	0, OPOST },
423	{ "litout",	0, OPOST },
424	{ "-litout",	OPOST, 0 },
425	{ "ocrnl",	OCRNL, 0 },
426	{ "-ocrnl",	0, OCRNL },
427	{ "olcuc",	OLCUC, 0 },
428	{ "-olcuc",	0, OLCUC },
429	{ "onlcr",	ONLCR, 0 },
430	{ "-onlcr",	0, ONLCR },
431	{ "onlret",	ONLRET, 0 },
432	{ "-onlret",	0, ONLRET },
433	{ "onocr",	ONOCR, 0 },
434	{ "-onocr",	0, ONOCR },
435	{ "tabs",	0, OXTABS },		/* "preserve" tabs */
436	{ "-tabs",	OXTABS, 0 },
437	{ "oxtabs",	OXTABS, 0 },
438	{ "-oxtabs",	0, OXTABS },
439	{ NULL },
440};
441
442#define	CHK(s)	(*name == s[0] && !strcmp(name, s))
443
444static int
445msearch(char ***argvp, struct info *ip)
446{
447	const struct modes *mp;
448	char *name;
449
450	name = **argvp;
451
452	for (mp = cmodes; mp->name; ++mp)
453		if (CHK(mp->name)) {
454			ip->t.c_cflag &= ~mp->unset;
455			ip->t.c_cflag |= mp->set;
456			ip->set = 1;
457			return (1);
458		}
459	for (mp = imodes; mp->name; ++mp)
460		if (CHK(mp->name)) {
461			ip->t.c_iflag &= ~mp->unset;
462			ip->t.c_iflag |= mp->set;
463			ip->set = 1;
464			return (1);
465		}
466	for (mp = lmodes; mp->name; ++mp)
467		if (CHK(mp->name)) {
468			ip->t.c_lflag &= ~mp->unset;
469			ip->t.c_lflag |= mp->set;
470			ip->set = 1;
471			return (1);
472		}
473	for (mp = omodes; mp->name; ++mp)
474		if (CHK(mp->name)) {
475			ip->t.c_oflag &= ~mp->unset;
476			ip->t.c_oflag |= mp->set;
477			ip->set = 1;
478			return (1);
479		}
480	return (0);
481}
482
483/*
484 * from prinjob.c
485 */
486
487void
488lp_stty(struct lp_printer *lp, int fd)
489{
490	struct info i;
491	char **argv, **ap, **ep, *p, *val;
492
493	printer = lp->lp_name;
494
495	i.fd = fd;
496	i.set = i.wset = 0;
497	if (ioctl(i.fd, TIOCEXCL, (char *)0) == -1)
498		fatal("%s: ioctl(TIOCEXCL)", printer);
499
500	if (tcgetattr(i.fd, &i.t) == -1)
501		fatal("%s: tcgetattr", printer);
502
503	if (lp->lp_br > 0) {
504		cfsetspeed(&i.t, lp->lp_br);
505		i.set = 1;
506	}
507	if (lp->lp_ms) {
508		if (ioctl(i.fd, TIOCGETD, &i.ldisc) == -1)
509			fatal("%s: ioctl(TIOCGETD)", printer);
510
511		if (ioctl(i.fd, TIOCGWINSZ, &i.win) == -1)
512			log_warn("%s: ioctl(TIOCGWINSZ)", printer);
513
514		argv = calloc(256, sizeof(char *));
515		if (argv == NULL)
516			fatal("%s: malloc", printer);
517
518		p = strdup(lp->lp_ms);
519		ap = argv;
520		ep = argv + 255;
521		while ((val = strsep(&p, " \t,")) != NULL) {
522			if ((*ap++ = strdup(val)) == NULL)
523				fatal("%s: strdup", printer);
524			if (ap == ep)
525				fatal("%s: too many \"ms\" entries", printer);
526		}
527		*ap = NULL;
528
529		for (; *argv; ++argv) {
530			if (ksearch(&argv, &i))
531				continue;
532			if (msearch(&argv, &i))
533				continue;
534			log_warnx("%s: unknown stty flag: %s", printer, *argv);
535		}
536	}
537
538	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) == -1)
539		fatal("%s: tcsetattr", printer);
540
541	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) == -1)
542		log_warn("%s: ioctl(TIOCSWINSZ)", printer);
543}
544