--- pgp/src/random.c 2018/04/24 16:40:51 1.1.1.6 +++ pgp/src/random.c 2018/04/24 16:42:07 1.1.1.7 @@ -1,479 +1,479 @@ -/* - * True and cryptographic random number generation. - * - * (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. - * The author assumes no liability for damages resulting from the use - * of this software, even if the damage results from defects in this - * software. No warranty is expressed or implied. - * - * Note that while most PGP source modules bear Philip Zimmermann's - * copyright notice, many of them have been revised or entirely written - * by contributors who frequently failed to put their names in their - * code. Code that has been incorporated into PGP from other authors - * was either originally published in the public domain or is used with - * permission from the various authors. - * - * PGP is available for free to the public under certain restrictions. - * See the PGP User's Guide (included in the release package) for - * important information about licensing, patent restrictions on - * certain algorithms, trademarks, copyrights, and export controls. - * - * Written by Colin Plumb. - */ - -#include -#include -#include -#include -#include /* For SIGINT */ -#include - -#include "system.h" /* For ttycbreak, getch, etc. */ -#include "idea.h" -#include "md5.h" -#include "noise.h" -#include "language.h" -#include "random.h" -#include "fileio.h" /* For FOPRBIN */ -#include "pgp.h" /* For globalRandseedName */ -#include "randpool.h" - -static struct IdeaRandContext randContext; -static int randInitFlag = 0; - -/* - * Load the RNG state from the randseed.bin file on disk. - * Returns 0 on success, <0 on error. - */ -int -cryptRandOpen(void) -{ - byte buf[24]; - int len; - FILE *f; - - if (randInitFlag) - return 0; - - f = fopen(globalRandseedName, FOPRBIN); - if (!f) - return -1; - len = fread((char *)buf, 1, 24, f); - fclose(f); - ideaRandInit(&randContext, buf, buf+16); - randInitFlag = 1; - - return (len == 24) ? 0 : -1; -} - -void -cryptRandCreate(void) -{ - byte randbuf[24]; - int i; - FILE *f; - - for (i = 0; i < 24; i++) - randbuf[i] = trueRandByte(); - - ideaRandInit(&randContext, (byte *)randbuf, (byte *)randbuf+16); - randInitFlag = 1; - - f = fopen(globalRandseedName, FOPWBIN); - if (f) { - fwrite(randbuf, 1, 24, f); - fclose(f); - } - return; -} - -/* - * Encrypt the state of the PRNG with the given key. It is intended - * that this key is the md5 of the message to be encrypted, which is - * unpredictable to a would-be attacker who does not posess the message. - * This is simply a way to get some "random" bytes without a random - * number source. This "prewash" attempts to reduce the value of a - * captured randseed.bin file. - */ -void -cryptRandWash(byte const key[16]) -{ - struct IdeaCfbContext cfb; - - if (!randInitFlag) - cryptRandOpen(); - ideaCfbInit(&cfb, key); - ideaRandWash(&randContext, &cfb); - ideaCfbDestroy(&cfb); -} - -byte -cryptRandByte(void) -{ - if (!randInitFlag) - cryptRandOpen(); - return ideaRandByte(&randContext); -} - -/* - * Create a new RNG state, encrypt it with the session key, and save it - * out to disk. The RNG is re-initialized with the saved parameters. - * - * This uses the same key and initial vector (including the repeated - * check bytes and everything) that is used to encrypt the user's message. - * The hope is that this "postwash" renders it is at least as hard to - * derive old session keys from randseed.bin as it is to crack the the - * message directly. - * - * The purpose of using EXACTLY the same encryption is to make sure that - * there isn't related, but different data floating around that can be - * used for cryptanalysis. - */ -void -cryptRandSave(byte const key[16], byte const iv[8]) -{ - struct IdeaCfbContext cfb; - byte buf[24]; - FILE *f; - int i; - - /* This IV is EXACTLY the same as is used on bulk files. */ - memcpy(buf, iv, 8); - buf[8] = buf[6]; /* "check bytes" */ - buf[9] = buf[7]; - - ideaCfbInit(&cfb, key); - ideaCfbEncrypt(&cfb, buf, buf, 10); - ideaCfbSync(&cfb); - for (i = 0; i < 24; i++) - buf[i] = ideaRandByte(&randContext); - ideaCfbEncrypt(&cfb, buf, buf, 24); - ideaCfbDestroy(&cfb); - ideaRandInit(&randContext, buf, buf+16); - - f = fopen(globalRandseedName, FOPWBIN); - if (f) { - fwrite(buf, 1, 24, f); - fclose(f); - } -} - -/* - * True random bit handling - */ - -/* - * Because these truly random bytes are so unwieldy to accumulate, - * they can be regarded as a precious resource. Unfortunately, - * cryptographic key generation algorithms may require a great many - * random bytes while searching about for large random prime numbers. - * Fortunately, they need not all be truly random. We only need as - * many truly random bits as there are bits in the large prime we - * are searching for. These random bytes can be recycled and modified - * via pseudorandom numbers until the key is generated, without losing - * any of the integrity of randomness of the final key. - * - * The technique used is a pool of random numbers, which bytes are - * taken from successively and, when the end is reached, the pool - * is stirred using an irreversible hash function. Some (64 bytes) - * of the pool is not returned to ensure the sequence is not predictible - * from the values retriefed from trueRandByte(). To be precise, - * MD5Transform is used as a block cipher in CBC mode, and then the - * "key" (i.e. what is usually the material to be hashed) is overwritten - * with some of the just-generated random bytes. - * - * This is implemented in randpool.c; see that file for details. - * - * An estimate of the number of bits of true (Shannon) entropy in the - * pool is kept in trueRandBits. This is incremented when timed - * keystrokes are available, and decremented when bits are explicitly - * consumed for some purpose (such as prime generation) or another. - * - * trueRandFlush is called to obliterate traces of old random bits after - * prime generation is completed. (Primes are the most carefully-guarded - * values in PGP.) - */ - -static unsigned trueRandBits = 0; /* Bits of entropy in pool */ - -/* trueRandPending is bits to add to next accumulation request */ -static unsigned trueRandPending = 0; - -/* - * Destroys already-used random numbers. Ensures no sensitive data - * remains in memory that can be recovered later. This is called - * after RSA key generation, so speed is not critical, but security is. - * RSA key generation takes long enough that interrupts and other - * tasks are likely to have used a measurable and difficult-to-predict - * amount of real time, so there is some virtue in sampling the clocks - * with noise(). - */ -void -trueRandFlush(void) -{ - noise(); - randPoolStir(); /* Destroy evidence of what primes were generated */ - randPoolStir(); - randPoolStir(); - randPoolStir(); /* Make *really* certain */ -} - -/* - * "Consumes" count bits worth of entropy from the true random pool for some - * purpose, such as prime generation. - * - * Note that something like prime generation can end up calling trueRandByte - * more often than is implied by the count passed to trueRandClaim; this - * may happen if the random bit consumer is not perfectly efficient in its - * use of random bits. For example, if a search for a suitable prime fails, - * the easiest thing to do is to get another load of random bits and try - * again. It is perfectly acceptable if these bits are correlated with the - * bits used in the failed attempt, since they are discarded. - */ -void -trueRandConsume(unsigned count) -{ - assert (trueRandBits >= count); - trueRandBits -= count; -} - -/* - * Returns a truly random byte if any are available. It degenerates to - * a pseudorandom value if there are none. It is not an error to call - * this if none are available. For example, it is called when generating - * session keys to add to other sources of cryptographic random numbers. - * - * This forces an accumulation if any extra random bytes are pending. - */ -int -trueRandByte(void) -{ - if (trueRandPending) - trueRandAccum(0); - - return randPoolGetByte(); -} - -/* - * Given an event (typically a keystroke) coded by "event" - * at a random time, add all randomness to the random pool, - * compute a (conservative) estimate of the amount, add it - * to the pool, and return the amount of randomness. - * (The return value is just for informational purposes.) - * - * Double events are okay, but three in a row is considered - * suspiscous and the randomness is counted as 0. - */ -unsigned -trueRandEvent(int event) -{ - static int event1 = 0, event2 = 0; - word32 delta; - unsigned cbits; - - delta = noise(); - randPoolAddBytes((byte *)&event, sizeof(event)); - - if (event == event1 && event == event2) { - cbits = 0; - } else { - event2 = event1; - event1 = event; - - for (cbits = 0; delta; cbits++) - delta >>= 1; - - /* Excessive paranoia? */ - if (cbits > 8) - cbits = 8; - } - - trueRandBits += cbits; - if (trueRandBits > RANDPOOLBITS) - trueRandBits = RANDPOOLBITS; - - return cbits; -} - - -/* - * Since getting random bits from the keyboard requires user attention, - * we buffer up requests for them until we can do one big request. - */ -void -trueRandAccumLater(unsigned bitcount) -{ - trueRandPending += bitcount; /* Wow, that was easy! :-) */ -} - -static void flush_input(void); - -/* - * Performs an accumulation of random bits. As long as there are fewer bits - * in the buffer than are needed (the number passed, plus pending bits), - * prompt for more. - */ -void -trueRandAccum(unsigned count) /* Get this many random bits ready */ -{ - int c; -#if defined(MSDOS) || defined(__MSDOS__) - time_t timer; -#endif - - count += trueRandPending; /* Do deferred accumulation now */ - trueRandPending = 0; - - if (count > RANDPOOLBITS) - count = RANDPOOLBITS; - - if (trueRandBits >= count) - return; - - fprintf(stderr, -LANG("\nWe need to generate %u random bits. This is done by measuring the\ -\ntime intervals between your keystrokes. Please enter some random text\ -\non your keyboard until you hear the beep:\n"), count-trueRandBits); - - ttycbreak(); - - do { - /* display counter to show progress */ - fprintf(stderr,"\r%4d ", count-trueRandBits); - fflush(stderr); /* assure screen update */ - - flush_input(); /* If there's no delay, we can't use it */ - c = getch(); /* always wait for input */ -#ifdef MSDOS - if (c == 3) - breakHandler(SIGINT); - if (c == 0) - c = getch() + 256; -#endif - /* Print flag indicating acceptance (or not) */ - putc(trueRandEvent(c) ? '.' : '?' , stderr); - } while (trueRandBits < count); - - fputs("\r 0 *", stderr); - fputs(LANG("\007 -Enough, thank you.\n"), stderr); - -#if defined(MSDOS) || defined(__MSDOS__) - /* Wait until one full second has passed without keyboard input */ - do { - flush_input(); - sleep(1); - } while (kbhit()); -#else - sleep(1); - flush_input(); -#endif - - ttynorm(); -} - -#define BS 8 -#define LF 10 -#define CR 13 -#define DEL 127 - -#ifdef VMS -int putch(int); -#else -#define putch(c) putc(c, stderr) -#endif - -int -getstring(char *strbuf, unsigned maxlen, int echo) -/* Gets string from user, with no control characters allowed. - * Also accumulates random numbers. - * maxlen is max length allowed for string. - * echo is TRUE iff we should echo keyboard to screen. - * Returns null-terminated string in strbuf. - */ -{ - unsigned i; - char c; - - ttycbreak(); - -#ifdef AMIGA - aecho = (int)echo; - echo = FALSE; /* echo is done in getch */ -#endif /* AMIGA */ - - fflush(stdout); - i=0; - for (;;) { -#ifndef VMS - fflush(stderr); -#endif /* VMS */ - c = getch(); - trueRandEvent(c); -#ifdef VMS - if (c == 25) { /* Control-Y detected */ - ttynorm(); - breakHandler(SIGINT); - } -#endif /* VMS */ -#if defined(MSDOS) || defined (__MSDOS__) - if (c == 3) - breakHandler(SIGINT); -#endif - if (c==BS || c==DEL) { - if (i) { - if (echo) { - putch(BS); - putch(' '); - putch(BS); - } - i--; - } - continue; - } - if (c < ' ' && c != LF && c != CR) { - putch('\007'); -#if defined(MSDOS) || defined (__MSDOS__) - if (c == 3) - breakHandler(SIGINT); - if (c == 0) - getch(); /* Skip extended key codes */ -#endif - continue; - } - if (echo) - putch(c); - if (c==CR) { - if (echo) - putch(LF); - break; - } - if (c==LF) - break; - if (c=='\n') - break; - strbuf[i++] = c; - if (i >= maxlen) { - fputs("\007*\n", stderr); /* -Enough! */ -#if 0 - while (kbhit()) - getch(); /* clean up any typeahead */ -#endif - break; - } - } - strbuf[i] = '\0'; /* null termination of string */ - - ttynorm(); - - return(i); /* returns string length */ -} /* getstring */ - - -static void -flush_input(void) -{ /* on unix ttycbreak() will flush the input queue */ -#if defined(MSDOS) || defined (__MSDOS__) - while (kbhit()) /* flush typahead buffer */ - getch(); -#endif -} +/* + * True and cryptographic random number generation. + * + * (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + * The author assumes no liability for damages resulting from the use + * of this software, even if the damage results from defects in this + * software. No warranty is expressed or implied. + * + * Note that while most PGP source modules bear Philip Zimmermann's + * copyright notice, many of them have been revised or entirely written + * by contributors who frequently failed to put their names in their + * code. Code that has been incorporated into PGP from other authors + * was either originally published in the public domain or is used with + * permission from the various authors. + * + * PGP is available for free to the public under certain restrictions. + * See the PGP User's Guide (included in the release package) for + * important information about licensing, patent restrictions on + * certain algorithms, trademarks, copyrights, and export controls. + * + * Written by Colin Plumb. + */ + +#include +#include +#include +#include +#include /* For SIGINT */ +#include + +#include "system.h" /* For ttycbreak, getch, etc. */ +#include "idea.h" +#include "md5.h" +#include "noise.h" +#include "language.h" +#include "random.h" +#include "fileio.h" /* For FOPRBIN */ +#include "pgp.h" /* For globalRandseedName */ +#include "randpool.h" + +static struct IdeaRandContext randContext; +static int randInitFlag = 0; + +/* + * Load the RNG state from the randseed.bin file on disk. + * Returns 0 on success, <0 on error. + */ +int +cryptRandOpen(void) +{ + byte buf[24]; + int len; + FILE *f; + + if (randInitFlag) + return 0; + + f = fopen(globalRandseedName, FOPRBIN); + if (!f) + return -1; + len = fread((char *)buf, 1, 24, f); + fclose(f); + ideaRandInit(&randContext, buf, buf+16); + randInitFlag = 1; + + return (len == 24) ? 0 : -1; +} + +void +cryptRandCreate(void) +{ + byte randbuf[24]; + int i; + FILE *f; + + for (i = 0; i < 24; i++) + randbuf[i] = trueRandByte(); + + ideaRandInit(&randContext, (byte *)randbuf, (byte *)randbuf+16); + randInitFlag = 1; + + f = fopen(globalRandseedName, FOPWBIN); + if (f) { + fwrite(randbuf, 1, 24, f); + fclose(f); + } + return; +} + +/* + * Encrypt the state of the PRNG with the given key. It is intended + * that this key is the md5 of the message to be encrypted, which is + * unpredictable to a would-be attacker who does not posess the message. + * This is simply a way to get some "random" bytes without a random + * number source. This "prewash" attempts to reduce the value of a + * captured randseed.bin file. + */ +void +cryptRandWash(byte const key[16]) +{ + struct IdeaCfbContext cfb; + + if (!randInitFlag) + cryptRandOpen(); + ideaCfbInit(&cfb, key); + ideaRandWash(&randContext, &cfb); + ideaCfbDestroy(&cfb); +} + +byte +cryptRandByte(void) +{ + if (!randInitFlag) + cryptRandOpen(); + return ideaRandByte(&randContext); +} + +/* + * Create a new RNG state, encrypt it with the session key, and save it + * out to disk. The RNG is re-initialized with the saved parameters. + * + * This uses the same key and initial vector (including the repeated + * check bytes and everything) that is used to encrypt the user's message. + * The hope is that this "postwash" renders it is at least as hard to + * derive old session keys from randseed.bin as it is to crack the the + * message directly. + * + * The purpose of using EXACTLY the same encryption is to make sure that + * there isn't related, but different data floating around that can be + * used for cryptanalysis. + */ +void +cryptRandSave(byte const key[16], byte const iv[8]) +{ + struct IdeaCfbContext cfb; + byte buf[24]; + FILE *f; + int i; + + /* This IV is EXACTLY the same as is used on bulk files. */ + memcpy(buf, iv, 8); + buf[8] = buf[6]; /* "check bytes" */ + buf[9] = buf[7]; + + ideaCfbInit(&cfb, key); + ideaCfbEncrypt(&cfb, buf, buf, 10); + ideaCfbSync(&cfb); + for (i = 0; i < 24; i++) + buf[i] = ideaRandByte(&randContext); + ideaCfbEncrypt(&cfb, buf, buf, 24); + ideaCfbDestroy(&cfb); + ideaRandInit(&randContext, buf, buf+16); + + f = fopen(globalRandseedName, FOPWBIN); + if (f) { + fwrite(buf, 1, 24, f); + fclose(f); + } +} + +/* + * True random bit handling + */ + +/* + * Because these truly random bytes are so unwieldy to accumulate, + * they can be regarded as a precious resource. Unfortunately, + * cryptographic key generation algorithms may require a great many + * random bytes while searching about for large random prime numbers. + * Fortunately, they need not all be truly random. We only need as + * many truly random bits as there are bits in the large prime we + * are searching for. These random bytes can be recycled and modified + * via pseudorandom numbers until the key is generated, without losing + * any of the integrity of randomness of the final key. + * + * The technique used is a pool of random numbers, which bytes are + * taken from successively and, when the end is reached, the pool + * is stirred using an irreversible hash function. Some (64 bytes) + * of the pool is not returned to ensure the sequence is not predictible + * from the values retriefed from trueRandByte(). To be precise, + * MD5Transform is used as a block cipher in CBC mode, and then the + * "key" (i.e. what is usually the material to be hashed) is overwritten + * with some of the just-generated random bytes. + * + * This is implemented in randpool.c; see that file for details. + * + * An estimate of the number of bits of true (Shannon) entropy in the + * pool is kept in trueRandBits. This is incremented when timed + * keystrokes are available, and decremented when bits are explicitly + * consumed for some purpose (such as prime generation) or another. + * + * trueRandFlush is called to obliterate traces of old random bits after + * prime generation is completed. (Primes are the most carefully-guarded + * values in PGP.) + */ + +static unsigned trueRandBits = 0; /* Bits of entropy in pool */ + +/* trueRandPending is bits to add to next accumulation request */ +static unsigned trueRandPending = 0; + +/* + * Destroys already-used random numbers. Ensures no sensitive data + * remains in memory that can be recovered later. This is called + * after RSA key generation, so speed is not critical, but security is. + * RSA key generation takes long enough that interrupts and other + * tasks are likely to have used a measurable and difficult-to-predict + * amount of real time, so there is some virtue in sampling the clocks + * with noise(). + */ +void +trueRandFlush(void) +{ + noise(); + randPoolStir(); /* Destroy evidence of what primes were generated */ + randPoolStir(); + randPoolStir(); + randPoolStir(); /* Make *really* certain */ +} + +/* + * "Consumes" count bits worth of entropy from the true random pool for some + * purpose, such as prime generation. + * + * Note that something like prime generation can end up calling trueRandByte + * more often than is implied by the count passed to trueRandClaim; this + * may happen if the random bit consumer is not perfectly efficient in its + * use of random bits. For example, if a search for a suitable prime fails, + * the easiest thing to do is to get another load of random bits and try + * again. It is perfectly acceptable if these bits are correlated with the + * bits used in the failed attempt, since they are discarded. + */ +void +trueRandConsume(unsigned count) +{ + assert (trueRandBits >= count); + trueRandBits -= count; +} + +/* + * Returns a truly random byte if any are available. It degenerates to + * a pseudorandom value if there are none. It is not an error to call + * this if none are available. For example, it is called when generating + * session keys to add to other sources of cryptographic random numbers. + * + * This forces an accumulation if any extra random bytes are pending. + */ +int +trueRandByte(void) +{ + if (trueRandPending) + trueRandAccum(0); + + return randPoolGetByte(); +} + +/* + * Given an event (typically a keystroke) coded by "event" + * at a random time, add all randomness to the random pool, + * compute a (conservative) estimate of the amount, add it + * to the pool, and return the amount of randomness. + * (The return value is just for informational purposes.) + * + * Double events are okay, but three in a row is considered + * suspiscous and the randomness is counted as 0. + */ +unsigned +trueRandEvent(int event) +{ + static int event1 = 0, event2 = 0; + word32 delta; + unsigned cbits; + + delta = noise(); + randPoolAddBytes((byte *)&event, sizeof(event)); + + if (event == event1 && event == event2) { + cbits = 0; + } else { + event2 = event1; + event1 = event; + + for (cbits = 0; delta; cbits++) + delta >>= 1; + + /* Excessive paranoia? */ + if (cbits > 8) + cbits = 8; + } + + trueRandBits += cbits; + if (trueRandBits > RANDPOOLBITS) + trueRandBits = RANDPOOLBITS; + + return cbits; +} + + +/* + * Since getting random bits from the keyboard requires user attention, + * we buffer up requests for them until we can do one big request. + */ +void +trueRandAccumLater(unsigned bitcount) +{ + trueRandPending += bitcount; /* Wow, that was easy! :-) */ +} + +static void flush_input(void); + +/* + * Performs an accumulation of random bits. As long as there are fewer bits + * in the buffer than are needed (the number passed, plus pending bits), + * prompt for more. + */ +void +trueRandAccum(unsigned count) /* Get this many random bits ready */ +{ + int c; +#if defined(MSDOS) || defined(__MSDOS__) + time_t timer; +#endif + + count += trueRandPending; /* Do deferred accumulation now */ + trueRandPending = 0; + + if (count > RANDPOOLBITS) + count = RANDPOOLBITS; + + if (trueRandBits >= count) + return; + + fprintf(stderr, +LANG("\nWe need to generate %u random bits. This is done by measuring the\ +\ntime intervals between your keystrokes. Please enter some random text\ +\non your keyboard until you hear the beep:\n"), count-trueRandBits); + + ttycbreak(); + + do { + /* display counter to show progress */ + fprintf(stderr,"\r%4d ", count-trueRandBits); + fflush(stderr); /* assure screen update */ + + flush_input(); /* If there's no delay, we can't use it */ + c = getch(); /* always wait for input */ +#ifdef MSDOS + if (c == 3) + breakHandler(SIGINT); + if (c == 0) + c = getch() + 256; +#endif + /* Print flag indicating acceptance (or not) */ + putc(trueRandEvent(c) ? '.' : '?' , stderr); + } while (trueRandBits < count); + + fputs("\r 0 *", stderr); + fputs(LANG("\007 -Enough, thank you.\n"), stderr); + +#if defined(MSDOS) || defined(__MSDOS__) + /* Wait until one full second has passed without keyboard input */ + do { + flush_input(); + sleep(1); + } while (kbhit()); +#else + sleep(1); + flush_input(); +#endif + + ttynorm(); +} + +#define BS 8 +#define LF 10 +#define CR 13 +#define DEL 127 + +#ifdef VMS +int putch(int); +#else +#define putch(c) putc(c, stderr) +#endif + +int +getstring(char *strbuf, unsigned maxlen, int echo) +/* Gets string from user, with no control characters allowed. + * Also accumulates random numbers. + * maxlen is max length allowed for string. + * echo is TRUE iff we should echo keyboard to screen. + * Returns null-terminated string in strbuf. + */ +{ + unsigned i; + char c; + + ttycbreak(); + +#ifdef AMIGA + aecho = (int)echo; + echo = FALSE; /* echo is done in getch */ +#endif /* AMIGA */ + + fflush(stdout); + i=0; + for (;;) { +#ifndef VMS + fflush(stderr); +#endif /* VMS */ + c = getch(); + trueRandEvent(c); +#ifdef VMS + if (c == 25) { /* Control-Y detected */ + ttynorm(); + breakHandler(SIGINT); + } +#endif /* VMS */ +#if defined(MSDOS) || defined (__MSDOS__) + if (c == 3) + breakHandler(SIGINT); +#endif + if (c==BS || c==DEL) { + if (i) { + if (echo) { + putch(BS); + putch(' '); + putch(BS); + } + i--; + } + continue; + } + if (c < ' ' && c != LF && c != CR) { + putch('\007'); +#if defined(MSDOS) || defined (__MSDOS__) + if (c == 3) + breakHandler(SIGINT); + if (c == 0) + getch(); /* Skip extended key codes */ +#endif + continue; + } + if (echo) + putch(c); + if (c==CR) { + if (echo) + putch(LF); + break; + } + if (c==LF) + break; + if (c=='\n') + break; + strbuf[i++] = c; + if (i >= maxlen) { + fputs("\007*\n", stderr); /* -Enough! */ +#if 0 + while (kbhit()) + getch(); /* clean up any typeahead */ +#endif + break; + } + } + strbuf[i] = '\0'; /* null termination of string */ + + ttynorm(); + + return(i); /* returns string length */ +} /* getstring */ + + +static void +flush_input(void) +{ /* on unix ttycbreak() will flush the input queue */ +#if defined(MSDOS) || defined (__MSDOS__) + while (kbhit()) /* flush typahead buffer */ + getch(); +#endif +}