1: #include "parms.h" 2: #include "structs.h" 3: 4: #ifdef RCSIDENT 5: static char rcsid[] = "$Header: miscio.c,v 1.7.0.4 96/3/21 23:06:01 notes Rel $"; 6: #endif RCSIDENT 7: 8: 9: /* miscio stuff: 10: * 11: * ttystrt/ttystop switch back and forth to character-at-a-time mode 12: * 13: * catchem makes our program ignore kills and coredumps. 14: * 15: * getnum is a char at a time input routine 16: * 17: * gchar sucks in 1 character. masks off parity. Uses raw mode to do this 18: * 19: */ 20: 21: 22: #include <signal.h> 23: #include <errno.h> 24: 25: 26: #ifdef USG /* Bell's Sys III and V */ 27: #include <termio.h> 28: struct termio tty, 29: otty; 30: #endif defined(USG) 31: 32: #if defined(V7) || defined(BSD4x) || defined(BSD2x) 33: /* Standard Bell V7 and Berkeley too */ 34: #include <sgtty.h> 35: int oldmode; /* prev mode bits */ 36: int oldlocalbits; /* prev local bits */ 37: struct sgttyb tty; 38: #endif 39: 40: char ttyerase, /* erase 1 char */ 41: ttykill; /* erase entire line */ 42: int echoctl = 0; /* as ^G or bell */ 43: int modeset = 0; /* == 1 if ttyflags messed with */ 44: short ospeed; /* for tputs padding */ 45: 46: 47: 48: ttystrt () 49: { 50: #if defined(BSD2x) || defined(BSD4x) 51: int localbits; 52: #endif 53: 54: echoctl = 0; /* default to non-echo */ 55: /* 56: * Grab the current tty state 57: */ 58: 59: #ifdef V7 /* V7 has simple tty controls */ 60: if (gtty (0, &tty) < 0) /* failure to grab */ 61: { 62: fprintf (stderr, "%s: Unable to gtty\n", Invokedas); 63: exit (1); 64: } 65: #endif defined(V7) 66: 67: #ifdef USG /* BTL SYS III & V */ 68: if (ioctl (0, TCGETA, &tty) < 0 || 69: ioctl (0, TCGETA, &otty) < 0) /* one failed */ 70: { 71: fprintf (stderr, "%s: Unable to get tty state\n", Invokedas); 72: exit (1); 73: } 74: #endif defined(USG) 75: 76: #if defined(BSD4x) || defined(BSD2x) 77: /* Berkeley Unices */ 78: if (ioctl (0, TIOCGETP, &tty) < 0 || 79: ioctl (0, TIOCLGET, &oldlocalbits) < 0) 80: { 81: fprintf (stderr, "%s: Unable to get tty states\n"); 82: exit (1); 83: } 84: if (oldlocalbits & LCTLECH) 85: echoctl = 1; 86: #endif 87: 88: /* 89: * Modify the state to what we want: cbreak, fix tildes for cursor 90: */ 91: 92: #if defined(USG) 93: tty.c_lflag &= NOT ICANON; 94: tty.c_cc[VEOF] = 1; 95: tty.c_cc[VEOL] = 1; 96: ospeed = tty.c_cflag & CBAUD; 97: ttyerase = tty.c_cc[VERASE]; /* grab erase char */ 98: ttykill = tty.c_cc[VKILL]; /* and kill char */ 99: #endif defined(USG) 100: 101: #if defined(BSD4x) || defined(BSD2x) || defined(V7) 102: oldmode = tty.sg_flags; 103: tty.sg_flags |= CBREAK; 104: ospeed = tty.sg_ospeed; /* speed of terminal */ 105: ttyerase = tty.sg_erase; /* grab erase character */ 106: ttykill = tty.sg_kill; /* and kill character */ 107: #endif 108: 109: 110: /* 111: * Now actually tell the system that we want it this way 112: */ 113: 114: #if defined(V7) 115: if (stty (0, &tty) < 0) /* failed */ 116: { 117: fprintf (stderr, "%s: Unable to stty\n", Invokedas); 118: exit (1); 119: } 120: #endif defined(V7) 121: 122: #if defined(USG) 123: if (ioctl (0, TCSETA, &tty) < 0) 124: { 125: fprintf (stderr, "%s: Unable to set tty state\n", Invokedas); 126: exit (1); 127: } 128: #endif defined(USG) 129: 130: modeset = 1; 131: cmstart (); /* so can cursor address reliably */ 132: } 133: 134: ttystop () 135: { 136: if (modeset) 137: { 138: #if defined(V7) || defined(BSD4x) || defined(BSD2x) 139: tty.sg_flags = oldmode; 140: #endif 141: 142: #if defined(V7) 143: if (stty (0, &tty) < 0) /* vanilla Version 7 */ 144: printf ("ttystop: stty"); /* cant use x cause he calls us */ 145: #endif defined(V7) 146: 147: #if defined(USG) 148: if (ioctl (0, TCSETA, &otty) < 0) /* Unix 4.0 */ 149: printf ("ttystop: stty"); /* cant use x cause he calls us */ 150: #endif defined(USG) 151: 152: #if defined(BSD4x) || defined(BSD2x) 153: if ((ioctl (0, TIOCSETN, &tty) < 0) || 154: (ioctl (0, TIOCLSET, &oldlocalbits) < 0)) 155: printf ("ttystop: stty"); /* cant use x cause he calls us */ 156: #endif 157: } 158: cmstop (); /* get out of cursor addressing mode */ 159: modeset = 0; 160: } 161: 162: 163: static int (*osigint) (), 164: (*osigquit) (); /* hold signal status */ 165: #if defined(SIGTSTP) 166: static int (*osigtstp) (); /* control-z job stop */ 167: #endif defined(SIGTSTP) 168: 169: catchint () 170: { 171: intflag = 1; 172: signal (SIGINT, catchint); /* fix em up again */ 173: #ifndef DEBUG 174: signal (SIGQUIT, catchint); 175: #endif DEBUG 176: } 177: 178: #if defined(SIGTSTP) 179: catchz () /* handle ^Z gracefully */ 180: { 181: int wasset; /* tty mode flag */ 182: 183: if (ignoresigs) /* if in critical section */ 184: { 185: signal (SIGTSTP, catchz); /* re-catch */ 186: return; /* and ignore */ 187: } 188: if ((wasset = modeset) != 0) /* want assignment */ 189: { 190: at (0, 1); /* go to bottom corner */ 191: fflush (stdout); 192: ttystop (); /* fix tty modes */ 193: } 194: 195: signal (SIGTSTP, SIG_DFL); /* make sure it nabs us */ 196: #if defined(BSD42) 197: /* 198: * since 4.2 Bsd blocks signals while we are handling them, we 199: * have to explicitly tell the kernel that we want the signals 200: * to come through. 201: * It would probably be more correct to only let some signals 202: * through instead of all. 203: */ 204: (void) sigsetmask(0L); /* pass signals */ 205: #endif BSD42 206: kill (0, SIGTSTP); /* halt myself */ 207: signal (SIGTSTP, catchz); /* ready to catch again */ 208: if (wasset) 209: ttystrt (); /* fix his tty */ 210: } 211: #endif defined(SIGTSTP) 212: 213: catchem () 214: { 215: osigint = signal (SIGINT, catchint); /* interrupts */ 216: #ifndef DEBUG 217: osigquit = signal (SIGQUIT, catchint); /* quits */ 218: #endif DEBUG 219: #if defined(SIGTSTP) 220: osigtstp = signal (SIGTSTP, catchz); /* control Z */ 221: #endif 222: } 223: 224: uncatchem () /* restore signal status */ 225: { 226: signal (SIGINT, osigint); 227: #ifndef DEBUG 228: signal (SIGQUIT, osigquit); 229: #endif DEBUG 230: #if defined(SIGTSTP) 231: signal (SIGTSTP, osigtstp); 232: #endif 233: } 234: 235: gchar () 236: /* 237: * Return next character from tty. 238: * this is all done in cbreak mode of course 239: */ 240: { 241: char c; 242: register int retcode; 243: fflush (stdout); /* get rid of what's there */ 244: while ((retcode = read (0, &c, 1)) <= 0) /* try reading */ 245: if (retcode == 0 || errno != EINTR) /* if bizarre */ 246: { 247: fprintf (stderr, "%s: Bad tty read\n", Invokedas); 248: exit (1); 249: } 250: intflag = 0; /* remove any pending */ 251: 252: return (c & 0177); 253: } 254: 255: /* 256: * getnum (c) 257: * grab a number from the terminal. "c" is the first digit o 258: * the number. 259: * 260: * Originally coded: Rob Kolstad Fall 1980 261: * Modified: Ray Essick (with help from Malcolm Slaney) 262: * July 1982 263: * to handle user defined erase and kill 264: * characters. 265: */ 266: 267: getnum (c) /* c is the initial character! */ 268: char c; 269: { 270: int num, 271: numin; 272: int i, 273: j; 274: 275: num = c - '0'; 276: numin = 1; 277: putc (c, stdout); 278: while (1) 279: { 280: c = gchar (); /* get next digit */ 281: if (c == ttyerase) /* want to erase? */ 282: { 283: if (numin > 0) /* if have some */ 284: { 285: switch (c) /* zap the char */ 286: { 287: case '\b': /* physical backspace */ 288: printf (echoctl ? "\b\b \b\b" : " \b"); 289: break; 290: default: 291: for (i = 0, j = widthch (c); i < j; i++) 292: printf ("\b \b"); 293: break; 294: } 295: printf ("\b \b"); /* and the digit */ 296: numin--; 297: num /= 10; /* correct num */ 298: } 299: else 300: { /* nothing to zap */ 301: switch (c) /* cover the erase */ 302: { 303: case '\b': 304: printf (echoctl ? "\b\b \b\b" : " "); 305: break; 306: default: 307: for (i = 0, j = widthch (c); i < j; i++) 308: printf ("\b \b"); /* erase it */ 309: break; 310: } 311: } 312: } 313: else 314: if (c == ttykill) 315: { 316: num = 0; /* blast it away */ 317: for (i = 0, j = widthch (c); i < j; i++) 318: printf ("\b \b"); 319: while (numin > 0) /* erase the screen */ 320: { 321: numin--; 322: printf ("\b \b"); 323: } 324: numin = 0; /* in case */ 325: } 326: else 327: switch (c) 328: { 329: case '\n': 330: case '\r': 331: return num; /* done */ 332: 333: default: 334: if (c < '0' || c > '9') 335: { 336: printf ("\10 \10\07"); 337: continue; 338: } 339: numin++; 340: num = 10 * num + (c - '0'); 341: break; 342: } 343: } 344: } 345: 346: /* 347: * gline( p, i) - suck a maximum of i characters from the tty. 348: * do erase and kill processing. 349: * The line is terminated by the user typing a <cr> or <nl>. This 350: * character is converted to null and left on the end of the 351: * string returned. The count of characters (including the null 352: * terminator) is returned. 353: * The array passed in is assumed to have i+1 elements 354: * (enough for the characters plus the terminator) 355: * 356: * Original Coding: Ray Essick December 1981 357: * Repaired to use user's erase and kill characters 358: * Malcolm Slaney July 1982 359: * 360: */ 361: 362: gline (p, max) 363: char *p; 364: { 365: register int numin; 366: register char *q; /* pointer to buffer */ 367: register char c; /* hold the input character */ 368: register int i, 369: j; 370: 371: q = p; /* get base */ 372: numin = 0; 373: while (1) 374: { 375: c = gchar (); /* & flushes stdout */ 376: if (c == ttyerase) /* want to erase */ 377: { 378: if (numin > 0) 379: { 380: numin--; 381: q--; /* back up in buffer */ 382: switch (c) 383: { 384: case '\b': /* physical backspace */ 385: printf (echoctl ? "\b\b\b \b\b\b" : " \b"); 386: break; 387: default: /* anything else */ 388: j = widthch (c) + widthch (*q); 389: for (i = 0; i < j; i++) 390: printf ("\b \b"); 391: break; 392: } 393: } 394: else 395: { 396: switch (c) 397: { 398: case '\b': /* really backspaces */ 399: printf (echoctl ? "\b\b \b\b" : " "); 400: break; 401: default: 402: j = widthch (c); 403: for (i = 0; i < j; i++) 404: printf ("\b \b"); 405: break; 406: } 407: } 408: } 409: else 410: if (c == ttykill) 411: { 412: for (i = 0; i < widthch (c); i++) 413: printf ("\b \b"); 414: for (i = 0; i < numin; i++) /* for all our chars */ 415: { 416: --q; /* get to char */ 417: for (j = 0; j < widthch (*q); j++) /* zap it */ 418: printf ("\b \b"); 419: } 420: q = p; /* reset pointer */ 421: numin = 0; /* in case .. */ 422: } 423: else 424: switch (c) 425: { 426: case '\n': 427: case '\r': 428: if (numin >= max) /* should only ever be = */ 429: { 430: p[max] = '\0'; /* put a null at the end */ 431: return max + 1; /* which is how many we return */ 432: } 433: *q = '\0'; 434: numin++; 435: return numin; 436: 437: case '\\': /* escape character */ 438: printf ("\010"); /* back space to it */ 439: c = gchar (); /* grab escaped character */ 440: /* and fall through to default */ 441: 442: default: /* add character to buffer */ 443: if (numin < max) 444: { 445: *q++ = c; 446: numin++; 447: } 448: else 449: { 450: printf ("\10 \10"); /* show him I ignored char */ 451: } 452: break; 453: } 454: } 455: } 456: 457: askyn (p) char *p; /* returns y or n to the question */ 458: { 459: char c; /* return temp */ 460: printf ("%s", p); 461: while (1) 462: { 463: c = gchar (); 464: if (c == 'y' || c == 'n') 465: break; 466: printf ("\07 y or n please\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 467: } 468: return c; 469: } 470: 471: /* 472: * return 1 if there is input from the terminal, 473: * 0 otherwise. systems without the appropriate 474: * call should always return 0. 475: */ 476: isinput () 477: { 478: #ifdef FIONREAD /* BSD 4.1, 4.1a, 4.2 */ 479: long retval; 480: if (ioctl (0, FIONREAD, &retval)) 481: return 0; /* failed, say no input */ 482: return (retval != 0); /* count of characters */ 483: #endif FIONREAD 484: 485: return 0; /* for other systems */ 486: } 487: 488: /* 489: * mapch(c) char c; 490: * 491: * prints control characters as ^x style. 492: * others as normal. 493: */ 494: mapch (c) 495: char c; 496: { 497: if (c < 040) 498: { 499: putchar ('^'); 500: putchar (c | 0100); /* make visible */ 501: } 502: else 503: if (c == 0177) 504: { 505: putchar ('^'); 506: putchar ('?'); 507: } 508: else 509: putchar (c); 510: } 511: 512: /* 513: * widthch(c) 514: * 515: * returns the number of character positions this character uses 516: * when displayed. 517: */ 518: 519: int widthch (c) 520: char c; 521: { 522: if (c == '\b') 523: return (echoctl ? 2 : -1); 524: if (c < 040 || c == 0177) /* control character */ 525: return (2 * echoctl); /* 0 or 2 */ 526: return (1); /* normal character */ 527: }