|
|
1.1.1.9 ! root 1: /* ! 2: * True and cryptographic random number generation. ! 3: * ! 4: * (c) Copyright 1990-1996 by Philip Zimmermann. All rights reserved. ! 5: * The author assumes no liability for damages resulting from the use ! 6: * of this software, even if the damage results from defects in this ! 7: * software. No warranty is expressed or implied. ! 8: * ! 9: * Note that while most PGP source modules bear Philip Zimmermann's ! 10: * copyright notice, many of them have been revised or entirely written ! 11: * by contributors who frequently failed to put their names in their ! 12: * code. Code that has been incorporated into PGP from other authors ! 13: * was either originally published in the public domain or is used with ! 14: * permission from the various authors. ! 15: * ! 16: * PGP is available for free to the public under certain restrictions. ! 17: * See the PGP User's Guide (included in the release package) for ! 18: * important information about licensing, patent restrictions on ! 19: * certain algorithms, trademarks, copyrights, and export controls. ! 20: * ! 21: * Written by Colin Plumb. ! 22: */ ! 23: ! 24: #include <stdlib.h> ! 25: #include <stdio.h> ! 26: #include <string.h> ! 27: #include <assert.h> ! 28: #include <signal.h> /* For SIGINT */ ! 29: #include <time.h> ! 30: ! 31: #ifdef AMIGA /* Includes for timer -- RKNOP */ ! 32: #include <devices/timer.h> ! 33: #include <exec/memory.h> ! 34: #include <exec/libraries.h> ! 35: #include <proto/dos.h> ! 36: #include <proto/timer.h> ! 37: #include <proto/exec.h> ! 38: #endif /* AMIGA */ ! 39: ! 40: #ifdef __PUREC__ ! 41: #include <ext.h> ! 42: #endif ! 43: ! 44: #include "system.h" /* For ttycbreak, getch, etc. */ ! 45: #include "idea.h" ! 46: #include "md5.h" ! 47: #include "noise.h" ! 48: #include "language.h" ! 49: #include "random.h" ! 50: #include "fileio.h" /* For FOPRBIN */ ! 51: #include "pgp.h" /* For globalRandseedName */ ! 52: #include "randpool.h" ! 53: #ifdef MACTC5 ! 54: #include "Macutil2.h" ! 55: #include "Macutil3.h" ! 56: #include "TimeManager.h" ! 57: #include <unix.h> ! 58: #endif ! 59: ! 60: /* ! 61: * As of PGP 2.6.2, the randseed.bin file has been expanded. An explanation ! 62: * of how the whole thing works in in order, as people are always suspiscious ! 63: * of the random number generator. (After the xorbytes bug in 2.6, perhaps ! 64: * you should be.) There are two random number generators in PGP. One ! 65: * is the "cryptRand" family, which is based on X9.17, but uses IDEA instead ! 66: * of 2-key EDE triple-DES. This is the generator with a lot of peer review. ! 67: * The implementation is in idea.c. ! 68: * The second is the "trueRand" family, which attempt to accurately measure ! 69: * the entropy available from keyboard I/O. It keeps a lot more state. ! 70: * The implementation of this is in randpool.c. ! 71: * Originally, the trueRand generator was only used for generating primes, ! 72: * and the cryptRand for generating IDEA session keys. But things have ! 73: * become a bit more complex. In particular, the X9.17 specification ! 74: * wants a source of high-resolution time-of-day information, as a source ! 75: * of some "true" randomness to throw in. So we use the trueRand pool ! 76: * for that. ! 77: * The cryptRand functions keep a state file around, usually named ! 78: * randseed.bin, for a seed, as the X9.17 generator requires 24 bytes of ! 79: * known initial information. ! 80: * This data in this file is carefully "washed" before and after use to ! 81: * help ensure that if the file is captured or altered, the keys will ! 82: * not be too vulnerable. A washing consists of an encryption in PGP's ! 83: * usual CFB mode of the material coming from or being written to the ! 84: * randseed.bin file on disk. Assuming the cipher is strong, the effects ! 85: * of the wash are as difficult to predict as the key that is used is ! 86: * difficult to guess. ! 87: * Beforehand, we use the MD5 of the file being encrypted as an additional ! 88: * source of randomness (on the theory that an attacker trying to break ! 89: * a session key probably doesn't have the plaintext, or he wouldn't need ! 90: * to bother), and use that as an IDEA key (with a fixed IV of zero) ! 91: * to encrypt the randseed.bin data. ! 92: * After generating an IDEA key and IV, some more random bytes are generated ! 93: * to reinitialize randseed.bin, and these are encrypted in the same manner ! 94: * as the PGP message before being written to disk, on the assumption that ! 95: * if an attacker can decrypt that, they can decrypt the message directly ! 96: * and not bother attacking the randseed.bin file. ! 97: * The previous code only saved the 24 bytes needed by the X9.17 algorithm. ! 98: * But in 2.6.2, we decided to make the randseed.bin file substantially ! 99: * larger to hold more information that a would-be attacker must guess. ! 100: * There are two reasons for this: ! 101: * - Every time you run PGP, especially when responding to one of PGP's ! 102: * prompts, PGP samples the keystrokes for use as random numbers. ! 103: * It is a shame to throw this entropy (randomness) away just because ! 104: * there is no need for it in the current invocation of PGP. ! 105: * - A feature was added to 2.6.2 to generate files full of random bytes ! 106: * for other programs to use as key material. In this case, we haven't ! 107: * got a message we're encrypting to take some entropy from, and we may ! 108: * be asked to generate more than 24 random bytes, so there should be ! 109: * more than 24 bytes of seed material to work from. ! 110: * The implementation is added on to the previous one, to offer assurance ! 111: * that it is no weaker. ! 112: * When the cryptRand generator is opened, the file is washed (if possible) ! 113: * and the first 24 bytes are fed to the cryptographic RNG, while the ! 114: * remainder is added to the trueRand random number pool. ! 115: * When saving, the randseed.bin file is refilled with newly generated ! 116: * bytes, again washed if possible. It turns out (if you study the ! 117: * X9.17 RNG) that each of the 2^64 possible timestamp information ! 118: * values used in generating each 8 bytes of output generates a output ! 119: * value, so the entropy in the trueRand pool is put to good use; this ! 120: * is not just generating more data from 24 bytes of seed. ! 121: * The random pool is opened and saved with a washing key when ! 122: * generating a session key (see make_random_ideakey in crypto.c), ! 123: * but it is also opened (harmless if alreasy open) and saved ! 124: * (harmless if already saved) without a washing key in the exitPGP routine, ! 125: * to mix in any entropy collected in this invocation of PGP even if ! 126: * a session key was not generated. ! 127: */ ! 128: ! 129: /* ! 130: * The new randseed size, big enough to hold the full context of the cryptRand ! 131: * and trueRand generators. With the current RANDPOOLBITS of 3072 (384 bytes), ! 132: * that's 408 bytes. It's useless to make it any larger, although if ! 133: * RANDPOOLBITS is increased, it might be an idea to keep this smaller than ! 134: * one disk block on all systems (512 bytes is a good figure to use) ! 135: * so we don't change the space requirements for randseed.bin. ! 136: */ ! 137: #define RANDSEED_BYTES (RANDPOOLBITS/8 + 24) ! 138: /* Have we read in the randseed.bin file? */ ! 139: static boolean randSeedOpen = 0; ! 140: static struct IdeaRandContext randContext; ! 141: ! 142: /* ! 143: * Load the RNG state from the randseed.bin file on disk. ! 144: * Returns 0 on success, <0 on error. ! 145: * ! 146: * If cfb is non-zero, prewashes the data by encrypting with it. ! 147: */ ! 148: int ! 149: cryptRandOpen(struct IdeaCfbContext *cfb) ! 150: { ! 151: byte buf[256]; ! 152: int len; ! 153: FILE *f; ! 154: ! 155: if (randSeedOpen) ! 156: return 0; /* Already open */ ! 157: ! 158: f = fopen(globalRandseedName, FOPRBIN); ! 159: if (!f) ! 160: return -1; ! 161: ! 162: /* First get the bare minimum 24 bytes we need for the IDEA RNG */ ! 163: len = fread((char *)buf, 1, 24, f); ! 164: if (cfb) ! 165: ideaCfbEncrypt(cfb, buf, buf, 24); ! 166: ideaRandInit(&randContext, buf, buf+16); ! 167: randSeedOpen = TRUE; ! 168: if (len != 24) { /* Error */ ! 169: fclose(f); ! 170: return -1; ! 171: } ! 172: ! 173: /* Read any extra into the random pool */ ! 174: for (;;) { ! 175: len = fread((char *)buf, 1, sizeof(buf), f); ! 176: if (len <= 0) ! 177: break; ! 178: if (cfb) ! 179: ideaCfbEncrypt(cfb, buf, buf, len); ! 180: randPoolAddBytes(buf, len); ! 181: } ! 182: ! 183: fclose(f); ! 184: burn(buf); ! 185: #ifdef MACTC5 ! 186: PGPSetFinfo(globalRandseedName,'RSed','MPGP'); ! 187: #endif ! 188: return 0; ! 189: } ! 190: ! 191: /* Create a new state from the output of trueRandByte */ ! 192: void ! 193: cryptRandInit(struct IdeaCfbContext *cfb) ! 194: { ! 195: byte buf[24]; ! 196: int i; ! 197: ! 198: for (i = 0; i < sizeof(buf); i++) ! 199: buf[i] = trueRandByte(); ! 200: if (cfb) ! 201: ideaCfbEncrypt(cfb, buf, buf, sizeof(buf)); ! 202: ! 203: ideaRandInit(&randContext, buf, buf+16); ! 204: randSeedOpen = TRUE; ! 205: burn(buf); ! 206: } ! 207: ! 208: byte ! 209: cryptRandByte(void) ! 210: { ! 211: if (!randSeedOpen) ! 212: cryptRandOpen((struct IdeaCfbContext *)0); ! 213: return ideaRandByte(&randContext); ! 214: } ! 215: ! 216: /* ! 217: * Write out a file of random bytes. If cfb is defined, wash it with the ! 218: * cipher. ! 219: */ ! 220: int ! 221: cryptRandWriteFile(char const *name, struct IdeaCfbContext *cfb, unsigned bytes) ! 222: { ! 223: byte buf[256]; ! 224: FILE *f; ! 225: int i, len; ! 226: ! 227: f = fopen(name, FOPWBIN); ! 228: if (!f) ! 229: return -1; ! 230: ! 231: while (bytes) { ! 232: len = (bytes < sizeof(buf)) ? bytes : sizeof(buf); ! 233: for (i = 0; i < len; i++) ! 234: buf[i] = ideaRandByte(&randContext); ! 235: if (cfb) ! 236: ideaCfbEncrypt(cfb, buf, buf, len); ! 237: i = fwrite(buf, 1, len, f); ! 238: if (i < len) ! 239: break; ! 240: bytes -= len; ! 241: } ! 242: ! 243: #ifdef MACTC5 ! 244: PGPSetFinfo((char *)name,'RSed','MPGP'); ! 245: #endif ! 246: ! 247: return (fclose(f) != 0 || bytes != 0) ? -1 : 0; ! 248: } ! 249: ! 250: /* ! 251: * Create a new RNG state, encrypt it with the supplied key, and save it ! 252: * out to disk. ! 253: * ! 254: * When we encrypt a file, the saved data is "postwashed" using the ! 255: * same key and initial vector (including the repeated check bytes and ! 256: * everything) that is used to encrypt the user's message. ! 257: * The hope is that this "postwash" renders it is at least as hard to ! 258: * derive old session keys from randseed.bin as it is to crack the the ! 259: * message directly. ! 260: * ! 261: * The purpose of using EXACTLY the same encryption is to make sure that ! 262: * there isn't related, but different data floating around that can be ! 263: * used for cryptanalysis. ! 264: * ! 265: * This function is always called by exitPGP, without a washing encryption, ! 266: * so this function ignores that call if it has previously been called ! 267: * to save washed bytes. ! 268: */ ! 269: #ifdef MACTC5 ! 270: boolean savedwashed = FALSE; ! 271: #endif ! 272: ! 273: void ! 274: cryptRandSave(struct IdeaCfbContext *cfb) ! 275: { ! 276: #ifndef MACTC5 ! 277: static boolean savedwashed = FALSE; ! 278: #endif ! 279: ! 280: if (!randSeedOpen) ! 281: return; /* Do nothing */ ! 282: ! 283: if (cfb) ! 284: savedwashed = TRUE; ! 285: else if (savedwashed) ! 286: return; /* Don't re-save if it's already been saved washed. */ ! 287: ! 288: (void)cryptRandWriteFile(globalRandseedName, cfb, RANDSEED_BYTES); ! 289: #ifdef MACTC5 ! 290: PGPSetFinfo(globalRandseedName,'RSed','MPGP'); ! 291: #endif ! 292: } ! 293: ! 294: /* ! 295: * True random bit handling ! 296: */ ! 297: ! 298: /* ! 299: * Because these truly random bytes are so unwieldy to accumulate, ! 300: * they can be regarded as a precious resource. Unfortunately, ! 301: * cryptographic key generation algorithms may require a great many ! 302: * random bytes while searching about for large random prime numbers. ! 303: * Fortunately, they need not all be truly random. We only need as ! 304: * many truly random bits as there are bits in the large prime we ! 305: * are searching for. These random bytes can be recycled and modified ! 306: * via pseudorandom numbers until the key is generated, without losing ! 307: * any of the integrity of randomness of the final key. ! 308: * ! 309: * The technique used is a pool of random numbers, which bytes are ! 310: * taken from successively and, when the end is reached, the pool ! 311: * is stirred using an irreversible hash function. Some (64 bytes) ! 312: * of the pool is not returned to ensure the sequence is not predictible ! 313: * from the values retriefed from trueRandByte(). To be precise, ! 314: * MD5Transform is used as a block cipher in CBC mode, and then the ! 315: * "key" (i.e. what is usually the material to be hashed) is overwritten ! 316: * with some of the just-generated random bytes. ! 317: * ! 318: * This is implemented in randpool.c; see that file for details. ! 319: * ! 320: * An estimate of the number of bits of true (Shannon) entropy in the ! 321: * pool is kept in trueRandBits. This is incremented when timed ! 322: * keystrokes are available, and decremented when bits are explicitly ! 323: * consumed for some purpose (such as prime generation) or another. ! 324: * ! 325: * trueRandFlush is called to obliterate traces of old random bits after ! 326: * prime generation is completed. (Primes are the most carefully-guarded ! 327: * values in PGP.) ! 328: */ ! 329: ! 330: static unsigned trueRandBits = 0; /* Bits of entropy in pool */ ! 331: ! 332: #ifdef MACTC5 ! 333: #define CBITS 5 ! 334: #define TRByt 3 ! 335: #define TREvt 1 ! 336: ! 337: static void perturb(rbits) ! 338: int rbits; ! 339: { ! 340: spinner(); ! 341: randPoolAddBytes((byte *) seedBuffer, 8); ! 342: trueRandBits +=rbits; ! 343: if (trueRandBits > RANDPOOLBITS) ! 344: trueRandBits = RANDPOOLBITS; ! 345: return; ! 346: } ! 347: #endif ! 348: ! 349: /* trueRandPending is bits to add to next accumulation request */ ! 350: static unsigned trueRandPending = 0; ! 351: ! 352: /* ! 353: * Destroys already-used random numbers. Ensures no sensitive data ! 354: * remains in memory that can be recovered later. This is called ! 355: * after RSA key generation, so speed is not critical, but security is. ! 356: * RSA key generation takes long enough that interrupts and other ! 357: * tasks are likely to have used a measurable and difficult-to-predict ! 358: * amount of real time, so there is some virtue in sampling the clocks ! 359: * with noise(). ! 360: */ ! 361: void ! 362: trueRandFlush(void) ! 363: { ! 364: noise(); ! 365: randPoolStir(); /* Destroy evidence of what primes were generated */ ! 366: randPoolStir(); ! 367: randPoolStir(); ! 368: randPoolStir(); /* Make *really* certain */ ! 369: } ! 370: ! 371: /* ! 372: * "Consumes" count bits worth of entropy from the true random pool for some ! 373: * purpose, such as prime generation. ! 374: * ! 375: * Note that something like prime generation can end up calling trueRandByte ! 376: * more often than is implied by the count passed to trueRandClaim; this ! 377: * may happen if the random bit consumer is not perfectly efficient in its ! 378: * use of random bits. For example, if a search for a suitable prime fails, ! 379: * the easiest thing to do is to get another load of random bits and try ! 380: * again. It is perfectly acceptable if these bits are correlated with the ! 381: * bits used in the failed attempt, since they are discarded. ! 382: */ ! 383: void ! 384: trueRandConsume(unsigned count) ! 385: { ! 386: #ifdef MACTC5 ! 387: if( trueRandBits >= count ) ! 388: trueRandBits -= count; ! 389: else ! 390: trueRandBits = 0; ! 391: #else ! 392: assert (trueRandBits >= count); ! 393: trueRandBits -= count; ! 394: #endif ! 395: } ! 396: ! 397: /* ! 398: * Returns a truly random byte if any are available. It degenerates to ! 399: * a pseudorandom value if there are none. It is not an error to call ! 400: * this if none are available. For example, it is called when generating ! 401: * session keys to add to other sources of cryptographic random numbers. ! 402: * ! 403: * This forces an accumulation if any extra random bytes are pending. ! 404: */ ! 405: int ! 406: trueRandByte(void) ! 407: { ! 408: if (trueRandPending) ! 409: trueRandAccum(0); ! 410: #ifdef MACTC5 ! 411: while( trueRandBits < 8 ) { ! 412: perturb(TRByt); ! 413: spinner(); ! 414: } ! 415: #endif ! 416: ! 417: return randPoolGetByte(); ! 418: } ! 419: ! 420: /* ! 421: * Given an event (typically a keystroke) coded by "event" ! 422: * at a random time, add all randomness to the random pool, ! 423: * compute a (conservative) estimate of the amount, add it ! 424: * to the pool, and return the amount of randomness. ! 425: * (The return value is just for informational purposes.) ! 426: * ! 427: * Double events are okay, but three in a row is considered ! 428: * suspiscous and the randomness is counted as 0. ! 429: */ ! 430: unsigned ! 431: trueRandEvent(int event) ! 432: { ! 433: static int event1 = 0, event2 = 0; ! 434: word32 delta; ! 435: unsigned cbits; ! 436: ! 437: delta = noise(); ! 438: randPoolAddBytes((byte *)&event, sizeof(event)); ! 439: ! 440: #ifdef MACTC5 ! 441: perturb(TRByt); ! 442: #endif ! 443: ! 444: if (event == event1 && event == event2) { ! 445: cbits = 0; ! 446: } else { ! 447: event2 = event1; ! 448: event1 = event; ! 449: ! 450: for (cbits = 0; delta; cbits++) ! 451: delta >>= 1; ! 452: ! 453: /* Excessive paranoia? */ ! 454: #ifdef MACTC5 ! 455: if (cbits > CBITS) ! 456: cbits = CBITS; ! 457: #else ! 458: if (cbits > 8) ! 459: cbits = 8; ! 460: #endif ! 461: } ! 462: ! 463: trueRandBits += cbits; ! 464: if (trueRandBits > RANDPOOLBITS) ! 465: trueRandBits = RANDPOOLBITS; ! 466: ! 467: return cbits; ! 468: } ! 469: ! 470: ! 471: /* ! 472: * Since getting random bits from the keyboard requires user attention, ! 473: * we buffer up requests for them until we can do one big request. ! 474: */ ! 475: void ! 476: trueRandAccumLater(unsigned bitcount) ! 477: { ! 478: trueRandPending += bitcount; /* Wow, that was easy! :-) */ ! 479: #ifdef MACTC5 ! 480: spinner(); ! 481: #endif ! 482: } ! 483: ! 484: static void flush_input(void); ! 485: ! 486: #ifdef AMIGA /* Globals used for timing here and in noise.c - RKNOP 940613 */ ! 487: struct Library *TimerBase=NULL; ! 488: struct timerequest *TimerIO=NULL; ! 489: union { struct timeval t; ! 490: struct EClockVal e; ! 491: } time0,time1; ! 492: unsigned short use_eclock=0; ! 493: #endif /* AMIGA */ ! 494: ! 495: /* ! 496: * Performs an accumulation of random bits. As long as there are fewer bits ! 497: * in the buffer than are needed (the number passed, plus pending bits), ! 498: * prompt for more. ! 499: */ ! 500: void ! 501: trueRandAccum(unsigned count) /* Get this many random bits ready */ ! 502: { ! 503: int c; ! 504: #if defined(MSDOS) || defined(__MSDOS__) ! 505: time_t timer; ! 506: #endif ! 507: ! 508: count += trueRandPending; /* Do deferred accumulation now */ ! 509: trueRandPending = 0; ! 510: ! 511: if (count > RANDPOOLBITS) ! 512: count = RANDPOOLBITS; ! 513: ! 514: if (trueRandBits >= count) ! 515: return; ! 516: ! 517: fprintf(stderr, ! 518: LANG("\nWe need to generate %u random bits. This is done by measuring the\ ! 519: \ntime intervals between your keystrokes. Please enter some random text\ ! 520: \non your keyboard until you hear the beep:\n"), count-trueRandBits); ! 521: ! 522: ttycbreak(); ! 523: ! 524: #ifdef AMIGA /* RKNOP 940613 */ ! 525: TimerIO=(struct timerequest *)AllocMem(sizeof(struct timerequest), ! 526: MEMF_PUBLIC|MEMF_CLEAR); ! 527: if (TimerIO) ! 528: { if (OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *)TimerIO,0)) ! 529: TimerBase=NULL; ! 530: else ! 531: { TimerBase=(struct Library *)TimerIO->tr_node.io_Device; ! 532: if (TimerBase->lib_Version>=36) /* Use E-clock instead */ ! 533: { use_eclock=1; ! 534: CloseDevice((struct IORequest *)TimerIO); ! 535: if (!OpenDevice(TIMERNAME,UNIT_ECLOCK, ! 536: (struct IORequest *)TimerIO,0)) ! 537: TimerBase=(struct Library *)TimerIO->tr_node.io_Device; ! 538: else ! 539: TimerBase=NULL; ! 540: } ! 541: else use_eclock=0; ! 542: } ! 543: } ! 544: else TimerBase=NULL; ! 545: #endif /* AMIGA */ ! 546: ! 547: do { ! 548: /* display counter to show progress */ ! 549: fprintf(stderr,"\r%4d ", count-trueRandBits); ! 550: fflush(stderr); /* assure screen update */ ! 551: ! 552: flush_input(); /* If there's no delay, we can't use it */ ! 553: #ifdef MACTC5 ! 554: StartTMCounter(); ! 555: #endif ! 556: c = getch(); /* always wait for input */ ! 557: #ifdef MSDOS ! 558: if (c == 3) ! 559: breakHandler(SIGINT); ! 560: if (c == 0) ! 561: c = getch() + 256; ! 562: #endif ! 563: /* Print flag indicating acceptance (or not) */ ! 564: /* putc a macro, not safe to have function as an arg!! */ ! 565: fputc(trueRandEvent(c) ? '.' : '?' , stderr); ! 566: #ifdef MACTC5 ! 567: StopTMCounter(); ! 568: #endif ! 569: } while (trueRandBits < count); ! 570: ! 571: fputs("\r 0 *", stderr); ! 572: fputs(LANG("\007 -Enough, thank you.\n"), stderr); ! 573: ! 574: #if defined(MSDOS) || defined(__MSDOS__) ! 575: /* Wait until one full second has passed without keyboard input */ ! 576: do { ! 577: flush_input(); ! 578: sleep(1); ! 579: } while (kbhit()); ! 580: #else ! 581: #ifdef AMIGA /* Added RKNOP 940608 */ ! 582: Delay(50*1); /* dos.library function, wait 1 second */ ! 583: if (TimerBase) CloseDevice((struct IORequest *)TimerIO); ! 584: TimerBase=NULL; ! 585: if (TimerIO) FreeMem(TimerIO,sizeof(struct timerequest)); ! 586: TimerIO=NULL; ! 587: #else ! 588: sleep(1); ! 589: flush_input(); ! 590: #endif ! 591: #endif ! 592: ! 593: ttynorm(); ! 594: } ! 595: ! 596: #ifndef EBCDIC /* already defined in usuals.h */ ! 597: #define BS 8 ! 598: #define LF 10 ! 599: #define CR 13 ! 600: #define DEL 127 ! 601: #endif ! 602: ! 603: #ifdef VMS ! 604: int putch(int); ! 605: #else ! 606: #define putch(c) putc(c, stderr) ! 607: #endif ! 608: ! 609: int ! 610: getstring(char *strbuf, unsigned maxlen, int echo) ! 611: /* Gets string from user, with no control characters allowed. ! 612: * Also accumulates random numbers. ! 613: * maxlen is max length allowed for string. ! 614: * echo is TRUE iff we should echo keyboard to screen. ! 615: * Returns null-terminated string in strbuf. ! 616: */ ! 617: { ! 618: unsigned i; ! 619: char c; ! 620: ! 621: ttycbreak(); ! 622: ! 623: #ifdef AMIGA /* In case of -f (use stdio for plaintext input), ! 624: use ReqTools for input from the user */ ! 625: if (!IsInteractive(Input())) ! 626: return AmigaRequestString(strbuf,maxlen,echo); ! 627: #endif ! 628: ! 629: fflush(stdout); ! 630: i=0; ! 631: for (;;) { ! 632: #ifndef VMS ! 633: fflush(stderr); ! 634: #endif /* VMS */ ! 635: c = getch(); ! 636: trueRandEvent(c); ! 637: #ifdef VMS ! 638: if (c == 25) { /* Control-Y detected */ ! 639: ttynorm(); ! 640: breakHandler(SIGINT); ! 641: } ! 642: #endif /* VMS */ ! 643: #if defined(MSDOS) || defined (__MSDOS__) ! 644: if (c == 3) ! 645: breakHandler(SIGINT); ! 646: #endif ! 647: if (c==BS || c==DEL) { ! 648: if (i) { ! 649: if (echo) { ! 650: putch(BS); ! 651: putch(' '); ! 652: putch(BS); ! 653: } ! 654: i--; ! 655: } else { ! 656: putch('\007'); ! 657: } ! 658: continue; ! 659: } ! 660: if (c < ' ' && c != LF && c != CR) { ! 661: putch('\007'); ! 662: #if defined(MSDOS) || defined (__MSDOS__) ! 663: if (c == 3) ! 664: breakHandler(SIGINT); ! 665: if (c == 0) ! 666: getch(); /* Skip extended key codes */ ! 667: #endif ! 668: continue; ! 669: } ! 670: if (echo) ! 671: putch(c); ! 672: if (c==CR) { ! 673: if (echo) ! 674: putch(LF); ! 675: break; ! 676: } ! 677: if (c==LF) ! 678: break; ! 679: if (c=='\n') ! 680: break; ! 681: strbuf[i++] = c; ! 682: if (i >= maxlen) { ! 683: fputs("\007*\n", stderr); /* -Enough! */ ! 684: #if 0 ! 685: while (kbhit()) ! 686: getch(); /* clean up any typeahead */ ! 687: #endif ! 688: break; ! 689: } ! 690: } ! 691: strbuf[i] = '\0'; /* null termination of string */ ! 692: ! 693: ttynorm(); ! 694: ! 695: return(i); /* returns string length */ ! 696: } /* getstring */ ! 697: ! 698: ! 699: static void ! 700: flush_input(void) ! 701: { /* on unix ttycbreak() will flush the input queue */ ! 702: #if defined(MSDOS) || defined (__MSDOS__) || defined(MACTC5) ! 703: while (kbhit()) /* flush typahead buffer */ ! 704: getch(); ! 705: #endif ! 706: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.