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