1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2014, Joyent, Inc. All rights reserved.
26 */
27
28 /*
29 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
30 * All Rights Reserved
31 *
32 */
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <libintl.h>
38 #include <sys/types.h>
39 #include <ctype.h>
40 #include <termio.h>
41 #include <sys/stermio.h>
42 #include <sys/termiox.h>
43 #ifdef EUC
44 #include <sys/param.h>
45 #include <sys/stropts.h>
46 #include <sys/eucioctl.h>
47 #include <sys/csiioctl.h>
48 #include <sys/stream.h>
49 #include <sys/termios.h>
50 #include <sys/ldterm.h>
51 #include <getwidth.h>
52 #endif /* EUC */
53 #include "stty.h"
54 #include <locale.h>
55 #include <string.h>
56
57 static char *s_arg; /* s_arg: ptr to mode to be set */
58 static int match;
59 #ifdef EUC
60 static int parse_encoded(struct termios *, ldterm_cs_data_user_t *, int);
61 #else
62 static int parse_encoded(struct termios *);
63 #endif /* EUC */
64 static int eq(const char *string);
65 static int gct(char *cp, int term);
66
67 /* set terminal modes for supplied options */
68 char *
sttyparse(int argc,char * argv[],int term,struct termio * ocb,struct termios * cb,struct termiox * termiox,struct winsize * winsize,eucwidth_t * wp,struct eucioc * kwp,ldterm_cs_data_user_t * cswp,ldterm_cs_data_user_t * kcswp)69 sttyparse(int argc, char *argv[], int term, struct termio *ocb,
70 struct termios *cb, struct termiox *termiox, struct winsize *winsize
71 #ifdef EUC
72 /* */, eucwidth_t *wp, struct eucioc *kwp, ldterm_cs_data_user_t *cswp,
73 ldterm_cs_data_user_t *kcswp
74 #endif /* EUC */
75 /* */)
76 {
77 int i;
78
79 while (--argc > 0) {
80 s_arg = *++argv;
81 match = 0;
82 if (term & ASYNC) {
83 if (eq("erase") && --argc)
84 cb->c_cc[VERASE] = gct(*++argv, term);
85 else if (eq("intr") && --argc)
86 cb->c_cc[VINTR] = gct(*++argv, term);
87 else if (eq("quit") && --argc)
88 cb->c_cc[VQUIT] = gct(*++argv, term);
89 else if (eq("eof") && --argc)
90 cb->c_cc[VEOF] = gct(*++argv, term);
91 else if (eq("min") && --argc) {
92 if (isdigit((unsigned char)argv[1][0]))
93 cb->c_cc[VMIN] = atoi(*++argv);
94 else
95 cb->c_cc[VMIN] = gct(*++argv, term);
96 } else if (eq("eol") && --argc)
97 cb->c_cc[VEOL] = gct(*++argv, term);
98 else if (eq("eol2") && --argc)
99 cb->c_cc[VEOL2] = gct(*++argv, term);
100 else if (eq("time") && --argc) {
101 if (isdigit((unsigned char)argv[1][0]))
102 cb->c_cc[VTIME] = atoi(*++argv);
103 else
104 cb->c_cc[VTIME] = gct(*++argv, term);
105 } else if (eq("kill") && --argc)
106 cb->c_cc[VKILL] = gct(*++argv, term);
107 else if (eq("swtch") && --argc)
108 cb->c_cc[VSWTCH] = gct(*++argv, term);
109 if (match)
110 continue;
111 if (term & TERMIOS) {
112 if (eq("start") && --argc)
113 cb->c_cc[VSTART] = gct(*++argv, term);
114 else if (eq("stop") && --argc)
115 cb->c_cc[VSTOP] = gct(*++argv, term);
116 else if (eq("susp") && --argc)
117 cb->c_cc[VSUSP] = gct(*++argv, term);
118 else if (eq("dsusp") && --argc)
119 cb->c_cc[VDSUSP] = gct(*++argv, term);
120 else if (eq("rprnt") && --argc)
121 cb->c_cc[VREPRINT] = gct(*++argv, term);
122 else if (eq("reprint") && --argc)
123 cb->c_cc[VREPRINT] = gct(*++argv, term);
124 else if (eq("discard") && --argc)
125 cb->c_cc[VDISCARD] = gct(*++argv, term);
126 else if (eq("flush") && --argc)
127 cb->c_cc[VDISCARD] = gct(*++argv, term);
128 else if (eq("werase") && --argc)
129 cb->c_cc[VWERASE] = gct(*++argv, term);
130 else if (eq("lnext") && --argc)
131 cb->c_cc[VLNEXT] = gct(*++argv, term);
132 else if (eq("status") && --argc)
133 cb->c_cc[VSTATUS] = gct(*++argv, term);
134 }
135 if (match)
136 continue;
137 if (eq("ek")) {
138 cb->c_cc[VERASE] = CERASE;
139 cb->c_cc[VKILL] = CKILL;
140 } else if (eq("line") &&
141 !(term & TERMIOS) && --argc) {
142 ocb->c_line = atoi(*++argv);
143 continue;
144 } else if (eq("raw")) {
145 cb->c_cc[VMIN] = 1;
146 cb->c_cc[VTIME] = 0;
147 } else if (eq("-raw") | eq("cooked")) {
148 cb->c_cc[VEOF] = CEOF;
149 cb->c_cc[VEOL] = CNUL;
150 } else if (eq("sane")) {
151 cb->c_cc[VERASE] = CERASE;
152 cb->c_cc[VKILL] = CKILL;
153 cb->c_cc[VQUIT] = CQUIT;
154 cb->c_cc[VINTR] = CINTR;
155 cb->c_cc[VEOF] = CEOF;
156 cb->c_cc[VEOL] = CNUL;
157 cb->c_cc[VSTATUS] = CSTATUS;
158 /* SWTCH purposely not set */
159 #ifdef EUC
160 } else if (eq("defeucw")) {
161 kwp->eucw[0] = '\001';
162 kwp->eucw[1] =
163 (unsigned char)(wp->_eucw1 & 0177);
164 kwp->eucw[2] =
165 (unsigned char)(wp->_eucw2 & 0177);
166 kwp->eucw[3] =
167 (unsigned char)(wp->_eucw3 & 0177);
168
169 kwp->scrw[0] = '\001';
170 kwp->scrw[1] =
171 (unsigned char)(wp->_scrw1 & 0177);
172 kwp->scrw[2] =
173 (unsigned char)(wp->_scrw2 & 0177);
174 kwp->scrw[3] =
175 (unsigned char)(wp->_scrw3 & 0177);
176
177 (void) memcpy((void *)kcswp, (const void *)cswp,
178 sizeof (ldterm_cs_data_user_t));
179 #endif /* EUC */
180 } else if ((term & TERMIOS) && eq("ospeed") && --argc) {
181 s_arg = *++argv;
182 for (match = 0, i = 0; speeds[i].string; i++) {
183 if (eq(speeds[i].string)) {
184 (void) cfsetospeed(cb,
185 speeds[i].code);
186 break;
187 }
188 }
189 if (!match)
190 return (s_arg);
191 continue;
192
193 } else if ((term & TERMIOS) && eq("ispeed") && --argc) {
194 s_arg = *++argv;
195 for (match = 0, i = 0; speeds[i].string; i++) {
196 if (eq(speeds[i].string)) {
197 (void) cfsetispeed(cb,
198 speeds[i].code);
199 break;
200 }
201 }
202 if (!match)
203 return (s_arg);
204 continue;
205
206 } else {
207 for (match = 0, i = 0; speeds[i].string; i++) {
208 if (eq(speeds[i].string)) {
209 (void) cfsetospeed(cb,
210 speeds[i].code);
211 (void) cfsetispeed(cb,
212 speeds[i].code);
213 break;
214 }
215 }
216 }
217 }
218 if (!(term & ASYNC) && eq("ctab") && --argc) {
219 cb->c_cc[7] = gct(*++argv, term);
220 continue;
221 }
222
223 for (i = 0; imodes[i].string; i++)
224 if (eq(imodes[i].string)) {
225 cb->c_iflag &= ~imodes[i].reset;
226 cb->c_iflag |= imodes[i].set;
227 #ifdef EUC
228 if (wp->_multibyte &&
229 (eq("-raw") || eq("cooked") || eq("sane")))
230 cb->c_iflag &= ~ISTRIP;
231 #endif /* EUC */
232 }
233 if (term & TERMIOS) {
234 for (i = 0; nimodes[i].string; i++)
235 if (eq(nimodes[i].string)) {
236 cb->c_iflag &= ~nimodes[i].reset;
237 cb->c_iflag |= nimodes[i].set;
238 }
239 }
240
241 for (i = 0; omodes[i].string; i++)
242 if (eq(omodes[i].string)) {
243 cb->c_oflag &= ~omodes[i].reset;
244 cb->c_oflag |= omodes[i].set;
245 }
246 if (!(term & ASYNC) && eq("sane")) {
247 cb->c_oflag |= TAB3;
248 continue;
249 }
250 for (i = 0; cmodes[i].string; i++)
251 if (eq(cmodes[i].string)) {
252 cb->c_cflag &= ~cmodes[i].reset;
253 cb->c_cflag |= cmodes[i].set;
254 #ifdef EUC
255 if (wp->_multibyte &&
256 (eq("-raw") || eq("cooked") ||
257 eq("sane"))) {
258 cb->c_cflag &= ~(CS7|PARENB);
259 cb->c_cflag |= CS8;
260 }
261 #endif /* EUC */
262 }
263 if (term & TERMIOS)
264 for (i = 0; ncmodes[i].string; i++)
265 if (eq(ncmodes[i].string)) {
266 cb->c_cflag &= ~ncmodes[i].reset;
267 cb->c_cflag |= ncmodes[i].set;
268 }
269 for (i = 0; lmodes[i].string; i++)
270 if (eq(lmodes[i].string)) {
271 cb->c_lflag &= ~lmodes[i].reset;
272 cb->c_lflag |= lmodes[i].set;
273 }
274 if (term & TERMIOS)
275 for (i = 0; nlmodes[i].string; i++)
276 if (eq(nlmodes[i].string)) {
277 cb->c_lflag &= ~nlmodes[i].reset;
278 cb->c_lflag |= nlmodes[i].set;
279 }
280 if (term & FLOW) {
281 for (i = 0; hmodes[i].string; i++)
282 if (eq(hmodes[i].string)) {
283 termiox->x_hflag &= ~hmodes[i].reset;
284 termiox->x_hflag |= hmodes[i].set;
285 }
286 for (i = 0; clkmodes[i].string; i++)
287 if (eq(clkmodes[i].string)) {
288 termiox->x_cflag &= ~clkmodes[i].reset;
289 termiox->x_cflag |= clkmodes[i].set;
290 }
291
292 }
293
294 if (eq("rows") && --argc)
295 winsize->ws_row = atoi(*++argv);
296 else if ((eq("columns") || eq("cols")) && --argc)
297 winsize->ws_col = atoi(*++argv);
298 else if (eq("xpixels") && --argc)
299 winsize->ws_xpixel = atoi(*++argv);
300 else if (eq("ypixels") && --argc)
301 winsize->ws_ypixel = atoi(*++argv);
302
303 if (!match) {
304 #ifdef EUC
305 if (!parse_encoded(cb, kcswp, term)) {
306 #else
307 if (!parse_encoded(cb)) {
308 #endif /* EUC */
309 return (s_arg); /* parsing failed */
310 }
311 }
312 }
313 return ((char *)0);
314 }
315
316 static int
317 eq(const char *string)
318 {
319 int i;
320
321 if (!s_arg)
322 return (0);
323 i = 0;
324 loop:
325 if (s_arg[i] != string[i])
326 return (0);
327 if (s_arg[i++] != '\0')
328 goto loop;
329 match++;
330 return (1);
331 }
332
333 /* get pseudo control characters from terminal */
334 /* and convert to internal representation */
335 static int
336 gct(char *cp, int term)
337 {
338 int c;
339
340 c = *cp;
341 if (c == '^') {
342 c = *++cp;
343 if (c == '?')
344 c = 0177; /* map '^?' to 0177 */
345 else if (c == '-') {
346 /* map '^-' to undefined */
347 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200;
348 } else
349 c &= 037;
350 } else if (strcmp(cp, "undef") == 0) {
351 /* map "undef" to undefined */
352 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200;
353 }
354 return (c);
355 }
356
357 /* get modes of tty device and fill in applicable structures */
358 int
359 get_ttymode(int fd, struct termio *termio, struct termios *termios,
360 struct stio *stermio, struct termiox *termiox, struct winsize *winsize
361 #ifdef EUC
362 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp
363 #endif /* EUC */
364 /* */)
365 {
366 int i;
367 int term = 0;
368 #ifdef EUC
369 struct strioctl cmd;
370 #endif /* EUC */
371 if (ioctl(fd, STGET, stermio) == -1) {
372 term |= ASYNC;
373 if (ioctl(fd, TCGETS, termios) == -1) {
374 if (ioctl(fd, TCGETA, termio) == -1)
375 return (-1);
376 termios->c_lflag = termio->c_lflag;
377 termios->c_oflag = termio->c_oflag;
378 termios->c_iflag = termio->c_iflag;
379 termios->c_cflag = termio->c_cflag;
380 for (i = 0; i < NCC; i++)
381 termios->c_cc[i] = termio->c_cc[i];
382 } else
383 term |= TERMIOS;
384 } else {
385 termios->c_cc[7] = (unsigned)stermio->tab;
386 termios->c_lflag = stermio->lmode;
387 termios->c_oflag = stermio->omode;
388 termios->c_iflag = stermio->imode;
389 }
390
391 if (ioctl(fd, TCGETX, termiox) == 0)
392 term |= FLOW;
393
394 if (ioctl(fd, TIOCGWINSZ, winsize) == 0)
395 term |= WINDOW;
396 #ifdef EUC
397 cmd.ic_cmd = EUC_WGET;
398 cmd.ic_timout = 0;
399 cmd.ic_len = sizeof (struct eucioc);
400 cmd.ic_dp = (char *)kwp;
401
402 if (ioctl(fd, I_STR, &cmd) == 0)
403 term |= EUCW;
404
405 cmd.ic_cmd = CSDATA_GET;
406 cmd.ic_timout = 0;
407 cmd.ic_len = sizeof (ldterm_cs_data_user_t);
408 cmd.ic_dp = (char *)kcswp;
409
410 if (ioctl(fd, I_STR, &cmd) == 0)
411 term |= CSIW;
412 else
413 (void) memset((void *)kcswp, 0, sizeof (ldterm_cs_data_user_t));
414 #endif /* EUC */
415 return (term);
416 }
417
418 /* set tty modes */
419 int
420 set_ttymode(int fd, int term, struct termio *termio, struct termios *termios,
421 struct stio *stermio, struct termiox *termiox, struct winsize *winsize,
422 struct winsize *owinsize
423 #ifdef EUC
424 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp,
425 int invalid_ldterm_dat_file
426 #endif /* EUC */
427 /* */)
428 {
429 int i;
430 #ifdef EUC
431 struct strioctl cmd;
432 #endif /* EUC */
433
434 if (term & ASYNC) {
435 if (term & TERMIOS) {
436 if (ioctl(fd, TCSETSW, termios) == -1)
437 return (-1);
438 } else {
439 termio->c_lflag = termios->c_lflag;
440 termio->c_oflag = termios->c_oflag;
441 termio->c_iflag = termios->c_iflag;
442 termio->c_cflag = termios->c_cflag;
443 for (i = 0; i < NCC; i++)
444 termio->c_cc[i] = termios->c_cc[i];
445 if (ioctl(fd, TCSETAW, termio) == -1)
446 return (-1);
447 }
448
449 } else {
450 stermio->imode = termios->c_iflag;
451 stermio->omode = termios->c_oflag;
452 stermio->lmode = termios->c_lflag;
453 stermio->tab = termios->c_cc[7];
454 if (ioctl(fd, STSET, stermio) == -1)
455 return (-1);
456 }
457 if (term & FLOW) {
458 if (ioctl(fd, TCSETXW, termiox) == -1)
459 return (-1);
460 }
461 if ((owinsize->ws_col != winsize->ws_col ||
462 owinsize->ws_row != winsize->ws_row ||
463 owinsize->ws_xpixel != winsize->ws_xpixel ||
464 owinsize->ws_ypixel != winsize->ws_ypixel) &&
465 ioctl(0, TIOCSWINSZ, winsize) != 0)
466 return (-1);
467 #ifdef EUC
468 /*
469 * If the ldterm.dat file contains valid, non-EUC codeset info,
470 * send downstream CSDATA_SET. Otherwise, try EUC_WSET.
471 */
472 if (invalid_ldterm_dat_file) {
473 (void) fprintf(stderr, gettext(
474 "stty: can't set codeset width due to invalid ldterm.dat.\n"));
475 return (-1);
476 } else if ((term & CSIW) && kcswp->version) {
477 cmd.ic_cmd = CSDATA_SET;
478 cmd.ic_timout = 0;
479 cmd.ic_len = sizeof (ldterm_cs_data_user_t);
480 cmd.ic_dp = (char *)kcswp;
481 if (ioctl(fd, I_STR, &cmd) != 0) {
482 (void) fprintf(stderr, gettext(
483 "stty: can't set codeset width.\n"));
484 return (-1);
485 }
486 } else if (term & EUCW) {
487 cmd.ic_cmd = EUC_WSET;
488 cmd.ic_timout = 0;
489 cmd.ic_len = sizeof (struct eucioc);
490 cmd.ic_dp = (char *)kwp;
491 if (ioctl(fd, I_STR, &cmd) != 0) {
492 (void) fprintf(stderr, gettext(
493 "stty: can't set EUC codeset width.\n"));
494 return (-1);
495 }
496 }
497 #endif /* EUC */
498 return (0);
499 }
500
501 static int
502 parse_encoded(struct termios *cb
503 #ifdef EUC
504 /* */, ldterm_cs_data_user_t *kcswp, int term
505 #endif /* EUC */
506 /* */)
507 {
508 unsigned long grab[NUM_FIELDS];
509 int last, i;
510 #ifdef EUC
511 long l;
512 char s[3];
513 char *t;
514 char *r;
515 uchar_t *g;
516 ldterm_cs_data_user_t ecswp;
517 #endif /* EUC */
518
519 /*
520 * Although there are only 16 control chars defined as of April 1995,
521 * parse_encoded() and prencode() will not have to be changed if up to
522 * MAX_CC control chars are defined in the future.
523 * Scan the fields of "stty -g" output into the grab array.
524 * Set a total of NUM_FIELDS fields (NUM_MODES modes + MAX_CC
525 * control chars).
526 */
527 i = sscanf(s_arg, "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:"
528 "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx",
529 &grab[0], &grab[1], &grab[2], &grab[3], &grab[4], &grab[5],
530 &grab[6], &grab[7], &grab[8], &grab[9], &grab[10], &grab[11],
531 &grab[12], &grab[13], &grab[14], &grab[15], &grab[16], &grab[17],
532 &grab[18], &grab[19], &grab[20], &grab[21]);
533
534 if (i < 12)
535 return (0);
536 cb->c_iflag = grab[0];
537 cb->c_oflag = grab[1];
538 cb->c_cflag = grab[2];
539 cb->c_lflag = grab[3];
540
541 last = i - NUM_MODES;
542 for (i = 0; i < last; i++)
543 cb->c_cc[i] = (unsigned char) grab[i+NUM_MODES];
544
545 #ifdef EUC
546 /* This is to fulfill PSARC/1999/140 TCR2. */
547 if (term & CSIW) {
548 r = strdup(s_arg);
549 if (r == (char *)NULL) {
550 (void) fprintf(stderr, gettext(
551 "no more memory - try again later\n"));
552 return (0);
553 }
554 t = strtok(r, ":");
555 for (i = 0; t != NULL && i < 22; i++) {
556 t = strtok(NULL, ":");
557 }
558
559 if (t == NULL) {
560 free((void *)r);
561 return (0);
562 }
563 ecswp.version = (uchar_t)strtol(t, (char **)NULL, 16);
564 if (ecswp.version > LDTERM_DATA_VERSION ||
565 ecswp.version == 0) {
566 free((void *)r);
567 return (0);
568 }
569
570 if ((t = strtok(NULL, ":")) == NULL) {
571 free((void *)r);
572 return (0);
573 }
574 ecswp.codeset_type = (uchar_t)strtol(t, (char **)NULL, 16);
575 if (ecswp.codeset_type < LDTERM_CS_TYPE_MIN ||
576 ecswp.codeset_type > LDTERM_CS_TYPE_MAX) {
577 free((void *)r);
578 return (0);
579 }
580
581 if ((t = strtok(NULL, ":")) == NULL) {
582 free((void *)r);
583 return (0);
584 }
585 ecswp.csinfo_num = (uchar_t)strtol(t, (char **)NULL, 16);
586 if ((ecswp.codeset_type == LDTERM_CS_TYPE_EUC &&
587 ecswp.csinfo_num > 3) ||
588 (ecswp.codeset_type == LDTERM_CS_TYPE_PCCS &&
589 (ecswp.csinfo_num < 1 || ecswp.csinfo_num > 10))) {
590 free((void *)r);
591 return (0);
592 }
593
594 if ((t = strtok(NULL, ":")) == NULL) {
595 free((void *)r);
596 return (0);
597 }
598 s[2] = '\0';
599 for (i = 0; *t != 0 && i < MAXNAMELEN; i++) {
600 if (*(t + 1) == (char)NULL) {
601 free((void *)r);
602 return (0);
603 }
604 s[0] = *t++;
605 s[1] = *t++;
606 ecswp.locale_name[i] = (char)strtol(s, (char **)NULL,
607 16);
608 }
609 if (i >= MAXNAMELEN) {
610 free((void *)r);
611 return (0);
612 }
613 ecswp.locale_name[i] = '\0';
614
615 g = (uchar_t *)ecswp.eucpc_data;
616 for (i = 0; i < (LDTERM_CS_MAX_CODESETS * 4); i++) {
617 if ((t = strtok(NULL, ":")) == NULL) {
618 free((void *)r);
619 return (0);
620 }
621 l = strtol(t, (char **)NULL, 16);
622 if (l < 0 || l > 255) {
623 free((void *)r);
624 return (0);
625 }
626 *g++ = (uchar_t)l;
627 }
628
629 /* We got the 'ecswp' all filled up now; let's copy. */
630 (void) memcpy((void *)kcswp, (const void *)&ecswp,
631 sizeof (ldterm_cs_data_user_t));
632 }
633 #endif /* EUC */
634
635 return (1);
636 }
637