1/* 2 Copyright (c) 1990-2001 Info-ZIP. All rights reserved. 3 4 See the accompanying file LICENSE, version 2000-Apr-09 or later 5 (the contents of which are also included in zip.h) for terms of use. 6 If, for some reason, all these files are missing, the Info-ZIP license 7 also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html 8*/ 9/*--------------------------------------------------------------------------- 10 11 ttyio.c 12 13 This file contains routines for doing console input/output, including code 14 for non-echoing input. It is used by the encryption/decryption code but 15 does not contain any restricted code itself. This file is shared between 16 Info-ZIP's Zip and UnZip. 17 18 Contains: echo() (VMS only) 19 Echon() (Unix only) 20 Echoff() (Unix only) 21 screensize() (Unix only) 22 zgetch() (Unix, VMS, and non-Unix/VMS versions) 23 getp() ("PC," Unix/Atari/Be, VMS/VMCMS/MVS) 24 25 ---------------------------------------------------------------------------*/ 26 27#define __TTYIO_C /* identifies this source module */ 28 29#include "zip.h" 30#include "crypt.h" 31 32#if (CRYPT || (defined(UNZIP) && !defined(FUNZIP))) 33/* Non-echo console/keyboard input is needed for (en/de)cryption's password 34 * entry, and for UnZip(SFX)'s MORE and Pause features. 35 * (The corresponding #endif is found at the end of this module.) 36 */ 37 38#include "ttyio.h" 39 40#ifndef PUTC 41# define PUTC putc 42#endif 43 44#ifdef ZIP 45# ifdef GLOBAL /* used in Amiga system headers, maybe others too */ 46# undef GLOBAL 47# endif 48# define GLOBAL(g) g 49#else 50# define GLOBAL(g) G.g 51#endif 52 53#if (defined(__BEOS__) || defined(__HAIKU__)) /* why yes, we do */ 54# define HAVE_TERMIOS_H 55#endif 56 57#ifdef _POSIX_VERSION 58# ifndef USE_POSIX_TERMIOS 59# define USE_POSIX_TERMIOS /* use POSIX style termio (termios) */ 60# endif 61# ifndef HAVE_TERMIOS_H 62# define HAVE_TERMIOS_H /* POSIX termios.h */ 63# endif 64#endif /* _POSIX_VERSION */ 65 66#ifdef UNZIP /* Zip handles this with the unix/configure script */ 67# ifndef _POSIX_VERSION 68# if (defined(SYSV) || defined(CRAY)) && !defined(__MINT__) 69# ifndef USE_SYSV_TERMIO 70# define USE_SYSV_TERMIO 71# endif 72# ifdef COHERENT 73# ifndef HAVE_TERMIO_H 74# define HAVE_TERMIO_H 75# endif 76# ifdef HAVE_SYS_TERMIO_H 77# undef HAVE_SYS_TERMIO_H 78# endif 79# else /* !COHERENT */ 80# ifdef HAVE_TERMIO_H 81# undef HAVE_TERMIO_H 82# endif 83# ifndef HAVE_SYS_TERMIO_H 84# define HAVE_SYS_TERMIO_H 85# endif 86# endif /* ?COHERENT */ 87# endif /* (SYSV || CRAY) && !__MINT__ */ 88# endif /* !_POSIX_VERSION */ 89# if !(defined(BSD4_4) || defined(SYSV) || defined(__convexc__)) 90# ifndef NO_FCNTL_H 91# define NO_FCNTL_H 92# endif 93# endif /* !(BSD4_4 || SYSV || __convexc__) */ 94#endif /* UNZIP */ 95 96#ifdef HAVE_TERMIOS_H 97# ifndef USE_POSIX_TERMIOS 98# define USE_POSIX_TERMIOS 99# endif 100#endif 101 102#if (defined(HAVE_TERMIO_H) || defined(HAVE_SYS_TERMIO_H)) 103# ifndef USE_SYSV_TERMIO 104# define USE_SYSV_TERMIO 105# endif 106#endif 107 108#if (defined(UNZIP) && !defined(FUNZIP) && defined(UNIX) && defined(MORE)) 109# include <sys/ioctl.h> 110# define GOT_IOCTL_H 111 /* int ioctl OF((int, int, zvoid *)); GRR: may need for some systems */ 112#endif 113 114#ifndef HAVE_WORKING_GETCH 115 /* include system support for switching of console echo */ 116# ifdef VMS 117# include <descrip.h> 118# include <iodef.h> 119# include <ttdef.h> 120# include <starlet.h> 121# include <ssdef.h> 122# else /* !VMS */ 123# ifdef HAVE_TERMIOS_H 124# include <termios.h> 125# define sgttyb termios 126# define sg_flags c_lflag 127# define GTTY(f, s) tcgetattr(f, (zvoid *) s) 128# define STTY(f, s) tcsetattr(f, TCSAFLUSH, (zvoid *) s) 129# else /* !HAVE_TERMIOS_H */ 130# ifdef USE_SYSV_TERMIO /* Amdahl, Cray, all SysV? */ 131# ifdef HAVE_TERMIO_H 132# include <termio.h> 133# endif 134# ifdef HAVE_SYS_TERMIO_H 135# include <sys/termio.h> 136# endif 137# ifdef NEED_PTEM 138# include <sys/stream.h> 139# include <sys/ptem.h> 140# endif 141# define sgttyb termio 142# define sg_flags c_lflag 143# define GTTY(f,s) ioctl(f,TCGETA,(zvoid *)s) 144# define STTY(f,s) ioctl(f,TCSETAW,(zvoid *)s) 145# else /* !USE_SYSV_TERMIO */ 146# ifndef CMS_MVS 147# if (!defined(MINIX) && !defined(GOT_IOCTL_H)) 148# include <sys/ioctl.h> 149# endif 150# include <sgtty.h> 151# define GTTY gtty 152# define STTY stty 153# ifdef UNZIP 154 /* 155 * XXX : Are these declarations needed at all ???? 156 */ 157 /* 158 * GRR: let's find out... Hmmm, appears not... 159 int gtty OF((int, struct sgttyb *)); 160 int stty OF((int, struct sgttyb *)); 161 */ 162# endif 163# endif /* !CMS_MVS */ 164# endif /* ?USE_SYSV_TERMIO */ 165# endif /* ?HAVE_TERMIOS_H */ 166# ifndef NO_FCNTL_H 167# ifndef UNZIP 168# include <fcntl.h> 169# endif 170# else 171 char *ttyname OF((int)); 172# endif 173# endif /* ?VMS */ 174#endif /* !HAVE_WORKING_GETCH */ 175 176 177 178#ifndef HAVE_WORKING_GETCH 179#ifdef VMS 180 181static struct dsc$descriptor_s DevDesc = 182 {11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND"}; 183 /* {dsc$w_length, dsc$b_dtype, dsc$b_class, dsc$a_pointer}; */ 184 185/* 186 * Turn keyboard echoing on or off (VMS). Loosely based on VMSmunch.c 187 * and hence on Joe Meadows' file.c code. 188 */ 189int echo(opt) 190 int opt; 191{ 192 /* 193 * For VMS v5.x: 194 * IO$_SENSEMODE/SETMODE info: Programming, Vol. 7A, System Programming, 195 * I/O User's: Part I, sec. 8.4.1.1, 8.4.3, 8.4.5, 8.6 196 * sys$assign(), sys$qio() info: Programming, Vol. 4B, System Services, 197 * System Services Reference Manual, pp. sys-23, sys-379 198 * fixed-length descriptor info: Programming, Vol. 3, System Services, 199 * Intro to System Routines, sec. 2.9.2 200 * Greg Roelofs, 15 Aug 91 201 */ 202 203 short DevChan, iosb[4]; 204 long status; 205 unsigned long ttmode[2]; /* space for 8 bytes */ 206 207 208 /* assign a channel to standard input */ 209 status = sys$assign(&DevDesc, &DevChan, 0, 0); 210 if (!(status & 1)) 211 return status; 212 213 /* use sys$qio and the IO$_SENSEMODE function to determine the current 214 * tty status (for password reading, could use IO$_READVBLK function 215 * instead, but echo on/off will be more general) 216 */ 217 status = sys$qiow(0, DevChan, IO$_SENSEMODE, &iosb, 0, 0, 218 ttmode, 8, 0, 0, 0, 0); 219 if (!(status & 1)) 220 return status; 221 status = iosb[0]; 222 if (!(status & 1)) 223 return status; 224 225 /* modify mode buffer to be either NOECHO or ECHO 226 * (depending on function argument opt) 227 */ 228 if (opt == 0) /* off */ 229 ttmode[1] |= TT$M_NOECHO; /* set NOECHO bit */ 230 else 231 ttmode[1] &= ~((unsigned long) TT$M_NOECHO); /* clear NOECHO bit */ 232 233 /* use the IO$_SETMODE function to change the tty status */ 234 status = sys$qiow(0, DevChan, IO$_SETMODE, &iosb, 0, 0, 235 ttmode, 8, 0, 0, 0, 0); 236 if (!(status & 1)) 237 return status; 238 status = iosb[0]; 239 if (!(status & 1)) 240 return status; 241 242 /* deassign the sys$input channel by way of clean-up */ 243 status = sys$dassgn(DevChan); 244 if (!(status & 1)) 245 return status; 246 247 return SS$_NORMAL; /* we be happy */ 248 249} /* end function echo() */ 250 251 252/* 253 * Read a single character from keyboard in non-echoing mode (VMS). 254 * (returns EOF in case of errors) 255 */ 256int tt_getch() 257{ 258 short DevChan, iosb[4]; 259 long status; 260 char kbbuf[16]; /* input buffer with - some - excess length */ 261 262 /* assign a channel to standard input */ 263 status = sys$assign(&DevDesc, &DevChan, 0, 0); 264 if (!(status & 1)) 265 return EOF; 266 267 /* read a single character from SYS$COMMAND (no-echo) and 268 * wait for completion 269 */ 270 status = sys$qiow(0,DevChan, 271 IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR, 272 &iosb, 0, 0, 273 &kbbuf, 1, 0, 0, 0, 0); 274 if ((status&1) == 1) 275 status = iosb[0]; 276 277 /* deassign the sys$input channel by way of clean-up 278 * (for this step, we do not need to check the completion status) 279 */ 280 sys$dassgn(DevChan); 281 282 /* return the first char read, or EOF in case the read request failed */ 283 return (int)(((status&1) == 1) ? (uch)kbbuf[0] : EOF); 284 285} /* end function tt_getch() */ 286 287 288#else /* !VMS: basically Unix */ 289 290 291/* For VM/CMS and MVS, non-echo terminal input is not (yet?) supported. */ 292#ifndef CMS_MVS 293 294#ifdef ZIP /* moved to globals.h for UnZip */ 295 static int echofd=(-1); /* file descriptor whose echo is off */ 296#endif 297 298/* 299 * Turn echo off for file descriptor f. Assumes that f is a tty device. 300 */ 301void Echoff(__G__ f) 302 __GDEF 303 int f; /* file descriptor for which to turn echo off */ 304{ 305 struct sgttyb sg; /* tty device structure */ 306 307 GLOBAL(echofd) = f; 308 GTTY(f, &sg); /* get settings */ 309 sg.sg_flags &= ~ECHO; /* turn echo off */ 310 STTY(f, &sg); 311} 312 313/* 314 * Turn echo back on for file descriptor echofd. 315 */ 316void Echon(__G) 317 __GDEF 318{ 319 struct sgttyb sg; /* tty device structure */ 320 321 if (GLOBAL(echofd) != -1) { 322 GTTY(GLOBAL(echofd), &sg); /* get settings */ 323 sg.sg_flags |= ECHO; /* turn echo on */ 324 STTY(GLOBAL(echofd), &sg); 325 GLOBAL(echofd) = -1; 326 } 327} 328 329#endif /* !CMS_MVS */ 330#endif /* ?VMS */ 331 332 333#if (defined(UNZIP) && !defined(FUNZIP)) 334 335#if (defined(UNIX) || (defined(__BEOS__) || defined(__HAIKU__))) 336#ifdef MORE 337 338/* 339 * Get the number of lines on the output terminal. SCO Unix apparently 340 * defines TIOCGWINSZ but doesn't support it (!M_UNIX). 341 * 342 * GRR: will need to know width of terminal someday, too, to account for 343 * line-wrapping. 344 */ 345 346#if (defined(TIOCGWINSZ) && !defined(M_UNIX)) 347 348int screensize(tt_rows, tt_cols) 349 int *tt_rows; 350 int *tt_cols; 351{ 352 struct winsize wsz; 353#ifdef DEBUG_WINSZ 354 static int firsttime = TRUE; 355#endif 356 357 /* see termio(4) under, e.g., SunOS */ 358 if (ioctl(1, TIOCGWINSZ, &wsz) == 0) { 359#ifdef DEBUG_WINSZ 360 if (firsttime) { 361 firsttime = FALSE; 362 fprintf(stderr, "ttyio.c screensize(): ws_row = %d\n", 363 wsz.ws_row); 364 fprintf(stderr, "ttyio.c screensize(): ws_col = %d\n", 365 wsz.ws_col); 366 } 367#endif 368 /* number of rows */ 369 if (tt_rows != NULL) 370 *tt_rows = (int)((wsz.ws_row > 0) ? wsz.ws_row : 24); 371 /* number of columns */ 372 if (tt_cols != NULL) 373 *tt_cols = (int)((wsz.ws_col > 0) ? wsz.ws_col : 80); 374 return 0; /* signal success */ 375 } else { /* this happens when piping to more(1), for example */ 376#ifdef DEBUG_WINSZ 377 if (firsttime) { 378 firsttime = FALSE; 379 fprintf(stderr, 380 "ttyio.c screensize(): ioctl(TIOCGWINSZ) failed\n")); 381 } 382#endif 383 /* VT-100 assumed to be minimal hardware */ 384 if (tt_rows != NULL) 385 *tt_rows = 24; 386 if (tt_cols != NULL) 387 *tt_cols = 80; 388 return 1; /* signal failure */ 389 } 390} 391 392#else /* !TIOCGWINSZ: service not available, fall back to semi-bogus method */ 393 394int screensize(tt_rows, tt_cols) 395 int *tt_rows; 396 int *tt_cols; 397{ 398 char *envptr, *getenv(); 399 int n; 400 int errstat = 0; 401 402 /* GRR: this is overly simplistic, but don't have access to stty/gtty 403 * system anymore 404 */ 405 if (tt_rows != NULL) { 406 envptr = getenv("LINES"); 407 if (envptr == (char *)NULL || (n = atoi(envptr)) < 5) { 408 /* VT-100 assumed to be minimal hardware */ 409 *tt_rows = 24; 410 errstat = 1; /* signal failure */ 411 } else { 412 *tt_rows = n; 413 } 414 } 415 if (tt_cols != NULL) { 416 envptr = getenv("COLUMNS"); 417 if (envptr == (char *)NULL || (n = atoi(envptr)) < 5) { 418 *tt_cols = 80; 419 errstat = 1; /* signal failure */ 420 } else { 421 *tt_cols = n; 422 } 423 } 424 return errstat; 425} 426 427#endif /* ?(TIOCGWINSZ && !M_UNIX) */ 428#endif /* MORE */ 429 430 431/* 432 * Get a character from the given file descriptor without echo or newline. 433 */ 434int zgetch(__G__ f) 435 __GDEF 436 int f; /* file descriptor from which to read */ 437{ 438#if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS)) 439 char oldmin, oldtim; 440#endif 441 char c; 442 struct sgttyb sg; /* tty device structure */ 443 444 GTTY(f, &sg); /* get settings */ 445#if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS)) 446 oldmin = sg.c_cc[VMIN]; /* save old values */ 447 oldtim = sg.c_cc[VTIME]; 448 sg.c_cc[VMIN] = 1; /* need only one char to return read() */ 449 sg.c_cc[VTIME] = 0; /* no timeout */ 450 sg.sg_flags &= ~ICANON; /* canonical mode off */ 451#else 452 sg.sg_flags |= CBREAK; /* cbreak mode on */ 453#endif 454 sg.sg_flags &= ~ECHO; /* turn echo off, too */ 455 STTY(f, &sg); /* set cbreak mode */ 456 GLOBAL(echofd) = f; /* in case ^C hit (not perfect: still CBREAK) */ 457 458 read(f, &c, 1); /* read our character */ 459 460#if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS)) 461 sg.c_cc[VMIN] = oldmin; /* restore old values */ 462 sg.c_cc[VTIME] = oldtim; 463 sg.sg_flags |= ICANON; /* canonical mode on */ 464#else 465 sg.sg_flags &= ~CBREAK; /* cbreak mode off */ 466#endif 467 sg.sg_flags |= ECHO; /* turn echo on */ 468 STTY(f, &sg); /* restore canonical mode */ 469 GLOBAL(echofd) = -1; 470 471 return (int)(uch)c; 472} 473 474 475#else /* !UNIX && !__BEOS__ */ 476#ifndef VMS /* VMS supplies its own variant of getch() */ 477 478 479int zgetch(__G__ f) 480 __GDEF 481 int f; /* file descriptor from which to read (must be open already) */ 482{ 483 char c, c2; 484 485/*--------------------------------------------------------------------------- 486 Get a character from the given file descriptor without echo; can't fake 487 CBREAK mode (i.e., newline required), but can get rid of all chars up to 488 and including newline. 489 ---------------------------------------------------------------------------*/ 490 491 echoff(f); 492 read(f, &c, 1); 493 if (c != '\n') 494 do { 495 read(f, &c2, 1); /* throw away all other chars up thru newline */ 496 } while (c2 != '\n'); 497 echon(); 498 return (int)c; 499} 500 501#endif /* !VMS */ 502#endif /* ?(UNIX || __BEOS__) */ 503 504#endif /* UNZIP && !FUNZIP */ 505#endif /* !HAVE_WORKING_GETCH */ 506 507 508#if CRYPT /* getp() is only used with full encryption */ 509 510/* 511 * Simple compile-time check for source compatibility between 512 * zcrypt and ttyio: 513 */ 514#if (!defined(CR_MAJORVER) || (CR_MAJORVER < 2) || (CR_MINORVER < 7)) 515 error: This Info-ZIP tool requires zcrypt 2.7 or later. 516#endif 517 518/* 519 * Get a password of length n-1 or less into *p using the prompt *m. 520 * The entered password is not echoed. 521 */ 522 523#ifdef HAVE_WORKING_GETCH 524/* 525 * For the AMIGA, getch() is defined as Agetch(), which is in 526 * amiga/filedate.c; SAS/C 6.x provides a getch(), but since Agetch() 527 * uses the infrastructure that is already in place in filedate.c, it is 528 * smaller. With this function, echoff() and echon() are not needed. 529 * 530 * For the MAC, a non-echo macgetch() function is defined in the MacOS 531 * specific sources which uses the event handling mechanism of the 532 * desktop window manager to get a character from the keyboard. 533 * 534 * For the other systems in this section, a non-echo getch() function 535 * is either contained the C runtime library (conio package), or getch() 536 * is defined as an alias for a similar system specific RTL function. 537 */ 538 539#ifndef WINDLL /* WINDLL does not support a console interface */ 540#ifndef QDOS /* QDOS supplies a variant of this function */ 541 542/* This is the getp() function for all systems (with TTY type user interface) 543 * that supply a working `non-echo' getch() function for "raw" console input. 544 */ 545char *getp(__G__ m, p, n) 546 __GDEF 547 ZCONST char *m; /* prompt for password */ 548 char *p; /* return value: line input */ 549 int n; /* bytes available in p[] */ 550{ 551 char c; /* one-byte buffer for read() to use */ 552 int i; /* number of characters input */ 553 char *w; /* warning on retry */ 554 555 /* get password */ 556 w = ""; 557 do { 558 fputs(w, stderr); /* warning if back again */ 559 fputs(m, stderr); /* display prompt and flush */ 560 fflush(stderr); 561 i = 0; 562 do { /* read line, keeping first n characters */ 563 if ((c = (char)getch()) == '\r') 564 c = '\n'; /* until user hits CR */ 565 if (c == 8 || c == 127) { 566 if (i > 0) i--; /* the `backspace' and `del' keys works */ 567 } 568 else if (i < n) 569 p[i++] = c; /* truncate past n */ 570 } while (c != '\n'); 571 PUTC('\n', stderr); fflush(stderr); 572 w = "(line too long--try again)\n"; 573 } while (p[i-1] != '\n'); 574 p[i-1] = 0; /* terminate at newline */ 575 576 return p; /* return pointer to password */ 577 578} /* end function getp() */ 579 580#endif /* !QDOS */ 581#endif /* !WINDLL */ 582 583 584#else /* !HAVE_WORKING_GETCH */ 585 586 587#if (defined(UNIX) || defined(__MINT__) || (defined(__BEOS__) || defined(__HAIKU__))) 588 589#ifndef _PATH_TTY 590# ifdef __MINT__ 591# define _PATH_TTY ttyname(2) 592# else 593# define _PATH_TTY "/dev/tty" 594# endif 595#endif 596 597char *getp(__G__ m, p, n) 598 __GDEF 599 ZCONST char *m; /* prompt for password */ 600 char *p; /* return value: line input */ 601 int n; /* bytes available in p[] */ 602{ 603 char c; /* one-byte buffer for read() to use */ 604 int i; /* number of characters input */ 605 char *w; /* warning on retry */ 606 int f; /* file descriptor for tty device */ 607 608#ifdef PASSWD_FROM_STDIN 609 /* Read from stdin. This is unsafe if the password is stored on disk. */ 610 f = 0; 611#else 612 /* turn off echo on tty */ 613 614 if ((f = open(_PATH_TTY, 0)) == -1) 615 return NULL; 616#endif 617 /* get password */ 618 w = ""; 619 do { 620 fputs(w, stderr); /* warning if back again */ 621 fputs(m, stderr); /* prompt */ 622 fflush(stderr); 623 i = 0; 624 echoff(f); 625 do { /* read line, keeping n */ 626 read(f, &c, 1); 627 if (i < n) 628 p[i++] = c; 629 } while (c != '\n'); 630 echon(); 631 PUTC('\n', stderr); fflush(stderr); 632 w = "(line too long--try again)\n"; 633 } while (p[i-1] != '\n'); 634 p[i-1] = 0; /* terminate at newline */ 635 636#ifndef PASSWD_FROM_STDIN 637 close(f); 638#endif 639 640 return p; /* return pointer to password */ 641 642} /* end function getp() */ 643 644#endif /* UNIX || __MINT__ || __BEOS__ */ 645 646 647 648#if (defined(VMS) || defined(CMS_MVS)) 649 650char *getp(__G__ m, p, n) 651 __GDEF 652 ZCONST char *m; /* prompt for password */ 653 char *p; /* return value: line input */ 654 int n; /* bytes available in p[] */ 655{ 656 char c; /* one-byte buffer for read() to use */ 657 int i; /* number of characters input */ 658 char *w; /* warning on retry */ 659 FILE *f; /* file structure for SYS$COMMAND device */ 660 661#ifdef PASSWD_FROM_STDIN 662 f = stdin; 663#else 664 if ((f = fopen(ctermid(NULL), "r")) == NULL) 665 return NULL; 666#endif 667 668 /* get password */ 669 fflush(stdout); 670 w = ""; 671 do { 672 if (*w) /* bug: VMS apparently adds \n to NULL fputs */ 673 fputs(w, stderr); /* warning if back again */ 674 fputs(m, stderr); /* prompt */ 675 fflush(stderr); 676 i = 0; 677 echoff(f); 678 do { /* read line, keeping n */ 679 if ((c = (char)getc(f)) == '\r') 680 c = '\n'; 681 if (i < n) 682 p[i++] = c; 683 } while (c != '\n'); 684 echon(); 685 PUTC('\n', stderr); fflush(stderr); 686 w = "(line too long--try again)\n"; 687 } while (p[i-1] != '\n'); 688 p[i-1] = 0; /* terminate at newline */ 689#ifndef PASSWD_FROM_STDIN 690 fclose(f); 691#endif 692 693 return p; /* return pointer to password */ 694 695} /* end function getp() */ 696 697#endif /* VMS || CMS_MVS */ 698#endif /* ?HAVE_WORKING_GETCH */ 699#endif /* CRYPT */ 700#endif /* CRYPT || (UNZIP && !FUNZIP) */ 701