Ressu 2.2 pari korjausta, –sample optio ja pääohjelman palastelua

Aiemmasta versiosta on korjattu pari bugia. Ensimmäisessä variaatioissa on vain pieniä ketjuja, ja 1,6 rajaa pienten ja suurien ketjujen välillä ei löydy. Tällöin lim1 ja lim2 kentät täytettiin joskus -1:llä. Tietue ensimmäisestä bugista:

round: 0 1:1 2:1 3:3 5:2 6:1 7:1 8:1 9:1 10:1 11:1 12:1 13:1 14:4 15:6 lim1:6 lim2:-1 clockbytes:244 rndbits:25

Toisessa bugissa on eripituisia variaatioita aina 1 kappaletta: jolloin isojen ketjujen ensimmäinen päätyy teoreettisiin bitteihin ja _genbytes rutiinista palataan liian nopeasti. Tässä esimerkki vanhasta ohjelmasta:

round: 1 1:1 2:1 3:1 4:1 5:1 6:1 7:1 8:1 9:1 10:1 12:1 14:1161 15:1096 lim1:1161 lim2:1161 clockbytes:32761 rndbits:1107
round: 1 1:1 2:1 3:1 5:1 6:1 7:1 8:1 9:1 10:1 12:1 13:1 14:1158 15:1099 lim1:1158 lim2:1158 clockbytes:32773 rndbits:1110
round: 1 1:1 2:1 4:1 5:1 8:1 9:1 10:1 11:1 13:1 14:1168 15:1090 lim1:1168 lim2:1168 clockbytes:32765 rndbits:1099

Muutoksissa on –sample optio, jolla voidaan tulostaa 8gb:n pituinen mallitiedosto, jolla voidaan testata satunnaisuutta esimerkiksi Dieharder ohjelmalla.

Lisäksi pääohjelmasta on palasteltu useampaan kertaan käytettyjä rutiineja omiksi funktioikseen.

Bugikorjaukset ovat lim=0 rivillä, if(prevbyte==-1 && f!=-1) rivillä ja if(f!=-1) rivillä.

Tässä toisaalta koodikorjaukset edellisistä bugeista ja muista muutoksista. Tässä aluksi koodi varsinaisesta korjatusta generaattorista:

unsigned char ressu_clockbyte() /* JariK 2013 */
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec & 0xff);
}

void ressu_genbytes_fast(int size, unsigned char *buffer)
{
  int c, d, e, byte;
  static int f = 0, prevbyte = -1, count = 0;

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e=((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e=((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f+buffer[d])%size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c,d,e;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  for(c=0; c<size; c++) {
    if(ressut_pos == 0) {
      if(ressut_first) {
	memset(ressut,0,ressut_bytes);
	ressut_first=0;
      }

      clockbytes=0;
      for(d=0;d<1024;d++)
	periods[d]=0;
      
      int rndbits=0;
      int lim, lim1, lim2;

      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

	ressu_genbytes_fast(ressut_bytes,ressut);

	lim=0;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if((prevf==-1 && f!=-1)|| (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	lim1=lim;
	
	// find next greater than lim

	f=-1;
	for(e=0;e<1024;e++) {
	  if(periods[e]>lim && (f==-1 || periods[e]<f))
	    f=periods[e];
	}
	if(f!=-1)
	  lim=f;

	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}

	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  rndbits=0;
	}
	
      } // for(d=0;
      if(stats) {
	fprintf(stdout,"round: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stdout," %d:%lu",e,periods[e]);
	}
	fprintf(stdout," lim1:%d",lim1);
	fprintf(stdout," lim2:%d",lim2);
	fprintf(stdout," clockbytes:%ld",clockbytes);
	fprintf(stdout," rndbits:%d",rndbits);
	fprintf(stdout,"\n");
	fflush(stdout);
      }
    } // if(ressut_pos == 0)
    ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_bytes;
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos+1) % ressut_bytes;
  } // for(c=0; c<size; c++)

  genbytes+=size;
}

Pääohjelmasta palastellut rutiinit: line_number_length() pääsi ulkoistettujen seuraan vaikka sillä oli vain yksi kutsukohta.

int line_number_length()
{
  int c;
  out_word(sizeof(linenobuf),linenobuf,"0123456789",lines-1);
  c=strlen(linenobuf);
  if(c<5)
    c=5;
  return(c);
}

void print_line_number()
{
  unsigned char linenobuf[1024];
  
  if(pchars==0 && !quiet && slineno) {
    sprintf(linenobuf,"%0*d",plinesdigits,plines);
    fprintf(stdout,"%s",linenobuf);
    pchars +=strlen(linenobuf);
    fprintf(stdout," ");
    pchars++;
  }
}

void print_spaces()
{
  if(sspace && pchars>0 && !quiet) {
    if(sspace==3 && pwords%2==0) {
      fprintf(stdout," ");
      pchars++;
    }
    fprintf(stdout," ");
    pchars++;
  }
}

void print_word(unsigned char *buf)
{
  if(size!=0) {
    if(!quiet)
      fprintf(stdout,"%*s",size,buf);
    pchars += size;
    pwords++;
  } else {
    if(!quiet)
      fprintf(stdout,"%s",buf);
    pchars += strlen(wordbuf);
    pwords++;
  }
}

Ja palastelun takia muutetut palat pääohjelmasta:

  for(;;) {

    if(!sort) { // also lotto

      pchars=0;
      pwords=0;

      while(pwords<linew) {
	readword(wordbuf);

	// in beginning of line, print line number

	print_line_number();
	
	// want to print spaces between "words"?

	print_spaces();
	
	// print word

	print_word(wordbuf);

      } // while(pwords<linew) {
    } else { // if(!sort)

      pchars=0;
      pwords=0;
      
      line_clear(&linecnt,&line);

      // fetch and save words on this row
      while(pwords<linew) {
	readword(wordbuf);

	if(line_add_string_sort(&linecnt,&line,wordbuf)) {
	  pwords++;
	}
      } // while(pwords<linew) {

      pchars=0;
      pwords=0;

      // print words on this row
      while(pwords<linew) {

	line_get_string(sizeof(wordbuf),wordbuf,pwords,line);

	// in beginning of line, print line number

	print_line_number();
	
	// want to print spaces between "words"?
	
	print_spaces();

	// print word
	
	print_word(wordbuf);

      } // while(pwords<linew) {
    } // if(!sort)

    // line full?
    
    if(!quiet) {
      fprintf(stdout,"\n");
    }
    plines++;
    if(sspace==3 && plines<lines && plines%5==0)
      fprintf(stdout,"\n");

    // all needed lines printed?

    if(plines >= lines)
      break;

  } // for(;;)

Lisätty sample optio, jolla voidaan tulostaa newressusample.rnd tiedosto satunnaisbittien testaamiseen vaikka dieharder ohjelmalla. –sample parametrin käyttämät palat parametrien käsittelyssä:

      } else if(!strcmp("--lotto",argv[c])) {
	sort=!sort;
	if(sspace==0)
	  sspace=1;
	//sort=1;
	//unique=1;

      } else if(!strcmp("--sample",argv[c])) {
	sample=!sample;
	
      } else if(!strcmp("--copyright",argv[c]) ||
	 !strcmp("--version",argv[c])) {
	fprintf(stderr,"%s",programname);
	fprintf(stderr,", %s",copyright);
	fprintf(stderr,"\n\n");
	help=1;
	
      

–sample esimerkkitiedoston tulostus: tiedoston koko on tässä 8GB, eli 1048576*4096*2

if(sample) {
    FILE *fp1;
    unsigned char buffer[4096];

    fp1=fopen("newressusample.rnd","w");
    for(c=0;c<1048576;c++) {
      ressu_genbytes(sizeof(buffer),buffer);
      fwrite(buffer,1,sizeof(buffer),fp1);
      ressu_genbytes(sizeof(buffer),buffer);
      fwrite(buffer,1,sizeof(buffer),fp1);
    }
    fclose(fp1);
    exit(0);
  }

Voit tulostaa samplen:

$ newressu --sample

komennolla ja dieharder ohjelmaa voit ajaa:

$ dieharder -a -g 201 -f newressusample.rnd

komennolla.

Tässä sorsa kokonaisuudessaan:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>

#include <string.h>
#include <ctype.h>
#include <math.h>
#include <sys/time.h>

unsigned char *procname;
static char *programname = "newressu version 2.2 ©";
static unsigned char *copyright = "Copyright (c) 2013-2021 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

unsigned long periods[1024];
unsigned long genbytes=0;
unsigned long clockbytes=0;

#define RESSUT_BYTES 4096
#define RESSU_BITS_NEEDED 128
#define RESSU_MIN_ROUNDS 2
#define RESSU_MIN_CLOCKBYTES 16384+1024

int ressu_bits_needed=RESSU_BITS_NEEDED;
int ressut_bytes = RESSUT_BYTES;

int stats=0;

unsigned char ressu_clockbyte() /* JariK 2013 */
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec & 0xff);
}

void ressu_genbytes_fast(int size, unsigned char *buffer)
{
  int c, d, e, byte;
  static int f = 0, prevbyte = -1, count = 0;

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e=((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e=((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f+buffer[d])%size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c,d,e;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  for(c=0; c<size; c++) {
    if(ressut_pos == 0) {
      if(ressut_first) {
	memset(ressut,0,ressut_bytes);
	ressut_first=0;
      }

      clockbytes=0;
      for(d=0;d<1024;d++)
	periods[d]=0;
      
      int rndbits=0;
      int lim, lim1, lim2;

      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

	ressu_genbytes_fast(ressut_bytes,ressut);

	lim=0;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if((prevf==-1 && f!=-1)|| (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	lim1=lim;
	
	// find next greater than lim

	f=-1;
	for(e=0;e<1024;e++) {
	  if(periods[e]>lim && (f==-1 || periods[e]<f))
	    f=periods[e];
	}
	if(f!=-1)
	  lim=f;

	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}

	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  rndbits=0;
	}
	
      } // for(d=0;
      if(stats) {
	fprintf(stdout,"round: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stdout," %d:%lu",e,periods[e]);
	}
	fprintf(stdout," lim1:%d",lim1);
	fprintf(stdout," lim2:%d",lim2);
	fprintf(stdout," clockbytes:%ld",clockbytes);
	fprintf(stdout," rndbits:%d",rndbits);
	fprintf(stdout,"\n");
	fflush(stdout);
      }
    } // if(ressut_pos == 0)
    ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_bytes;
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos+1) % ressut_bytes;
  } // for(c=0; c<size; c++)

  genbytes+=size;
}

#define DEBUG4 2

#ifdef DEBUG4
unsigned char cc[256*1048576]; // clock_chain
unsigned char *ccp;
int cc_bytes;
#endif

unsigned char ressu_clockbyte_debug() /* JariK 2013 */
{
  unsigned char c;
  struct timeval tv;
  gettimeofday(&tv, NULL);
  c=tv.tv_usec & 0xff;
#ifdef DEBUG4
  if(cc_bytes<sizeof(cc)) {
    *ccp++ = c;
    cc_bytes++;
  }
#endif
  return(c);
}

void ressu_genbytes_fast_debug(int size, unsigned char *buffer)
{
  int c, d, e, byte;
  static int f = 0, prevbyte = -1, count = 0;

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e=((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e=((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte_debug();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f+buffer[d])%size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

void ressu_genbytes_debug(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c,d,e;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  for(c=0; c<size; c++) {
    if(ressut_pos == 0) {
      if(ressut_first) {
	memset(ressut,0,ressut_bytes);
	ressut_first=0;
      }

      clockbytes=0;
      for(d=0;d<1024;d++)
	periods[d]=0;
      
#ifdef DEBUG4
      ccp=cc;
      cc_bytes=0;

      while(ressu_clockbyte_debug()!=0);
      while(ressu_clockbyte_debug()==0);

      ccp=cc;
      cc_bytes=0;
#endif

      int rndbits=0, rounds=0;
      int lim, lim1, lim2;

      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS; d++) {

	rounds++;
	
	ressu_genbytes_fast_debug(ressut_bytes,ressut);

	lim=0;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if((prevf==-1 && f!=-1)|| (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	lim1=lim;

	// find next greater than lim

	f=-1;
	for(e=0;e<1024;e++) {
	  if(periods[e]>lim && (f==-1 || periods[e]<f))
	    f=periods[e];
	}
	if(f!=-1)
	  lim=f;

	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}

	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  ccp=cc;
	  cc_bytes=0;
	  rndbits=0;
	}
      } // for(d=0;

#ifdef DEBUG4
      FILE *fp1;
      if((fp1=fopen("newressu.deb","a"))!=NULL) {
	//if(lim1==-1 || lim2==-1 || lim1==lim2) {
	for(d=0;d<32;d++)
	  fprintf(fp1,"%02x",ressut[d]);

	fprintf(fp1,", ressut_bytes: %d",ressut_bytes);
	fprintf(fp1,", ressu_bits_needed: %d",ressu_bits_needed);
	fprintf(fp1,", ressu_min_rounds: %d",RESSU_MIN_ROUNDS);

	fprintf(fp1,", clockbytes: %ld",clockbytes);
	
	unsigned long total=0;	
	fprintf(fp1,", fluctuations:");
	for(d=0;d<1024;d++) {
	  if(periods[d]!=0) {
	    fprintf(fp1," %d:%lu",d,periods[d]);
	    total+=(periods[d]*d);
	  }
	}
	fprintf(fp1,", total: %lu",total);
	fprintf(fp1,", limit1: %d",lim1);
	fprintf(fp1,", limit2: %d",lim2);
	fprintf(fp1,", limit: %d",lim);

	fprintf(fp1,", skipped:");
	for(d=0;d<1024;d++) {
	  if(periods[d]>=lim) {
	    fprintf(fp1," %d:%lu",d,periods[d]);
	  }
	}
	
	fprintf(fp1,", counted:");
	for(d=0;d<1024;d++) {
	  if(periods[d]>0 && periods[d]<lim) {
	    fprintf(fp1," %d:%lu",d,periods[d]);
	  }
	}	
	fprintf(fp1,", rndbits: %d",rndbits);
	fprintf(fp1,", rounds: %d",rounds);

	int prevbyte=-1, count=0, count2=0;
	unsigned char byte, small=-1;

	fprintf(fp1,", small chains:");
	for(d=0;d<cc_bytes;d++) {
	  byte=cc[d];
	  if(prevbyte==-1) {
	    prevbyte=byte;
	    count=1;
	  }
	  if(prevbyte!=byte) {
	    if(periods[count]>=lim) {
	      if(small==1 || small==-1)
		fprintf(fp1," *");
	      small=0;
	    } else small=1;
	    if(small) {
	      fprintf(fp1," %d",count);
	      count2++;
	    }
	    count=0;
	    prevbyte=byte;
	  }
	  count++;
	}
	if(small) {
	  fprintf(fp1," %d",count);
	  count2++;
	}
	fprintf(fp1,", count: %d",count2);

	fprintf(fp1,"\n");
	fflush(fp1);
	//}
	fclose(fp1);
      } // if((fp1=fopen
#endif
    } // if(ressut_pos == 0)
    ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_bytes;
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos+1) % ressut_bytes;
  } // for(c=0; c<size; c++)

  genbytes+=size;
}

static void readfile_xor(int len,
    unsigned char *buf,
    unsigned char *filename)
{
  int c, n, n2;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb"))
      != NULL) {
    while(len != 0) {
      n = (len < sizeof(temp)) ?
          len : sizeof(temp);
      n2=fread(temp, 1, n, fp1);
      for(c = 0; c < n2; c++)
        buf[c] ^= temp[c];
      len -= n2;
      buf += n2;
    }
    fclose(fp1);
  }
}

int input=0;

#define GENT_SIZE 2048
#define aUSE_RANDOM 2

#define aDEBUG9 2

char *randomgen[] = { "ressu", "debug","fast","single","4","5","6","7","urandom","random" };

int ressu_genbyte()
{
  int c;
  static unsigned char gent[GENT_SIZE];
  static unsigned int gent_pos=0; // Little bug
  unsigned char ch;

  if(input==0) { // ressu prod
    ressu_genbytes(sizeof(ch), &ch);
  } else if(input==1) { // ressu debug
    ressu_genbytes_debug(sizeof(ch), &ch);
  } else {
    if(gent_pos==0) {
      if(input==2) { // ressu_fast
	clockbytes=0;
	for(c=0; c<RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES;c++) {
	  ressu_genbytes_fast(sizeof(gent),gent);
	}
      } else if(input==3) { // ressu_single
	ressu_genbytes_fast(sizeof(gent),gent);
      } else if(input==8) { // urandom
	readfile_xor(sizeof(gent),gent,"/dev/urandom");
#ifdef USE_RANDOM
      } else if(input==9) { // random
	readfile_xor(sizeof(gent),gent,"/dev/random");
#endif
      }
    } // if(gent_pos==0
    ch=gent[gent_pos];
    gent_pos=(gent_pos+1)%sizeof(gent);
  }
  return(ch);
}

int ressu_genbyte_limit(int limit)
{
  int c;
  while((c=ressu_genbyte()) >= (256/limit)*limit);
  return(c%limit);
}

int ressu_genshort()
{
  return(ressu_genbyte() | ressu_genbyte()<<8);
}

int ressu_genshort_limit(int limit)
{
  int c;
  while((c=ressu_genshort()) >= (65536/limit)*limit);
  return(c%limit);
}

unsigned int ressu_genint()
{
  return(ressu_genshort() | ressu_genshort()<<16);
}

unsigned int ressu_genint_limit(unsigned long limit)
{
  unsigned int c;
  while((c=ressu_genint()) >= (((unsigned long)65536*65536)/limit)*limit);
  return(c%limit);
}

unsigned long ressu_genlong()
{
  return(((unsigned long)ressu_genint()) | ((unsigned long)ressu_genint())<<32);
}

unsigned long ressu_genlong_limit(unsigned long limit)
{
  unsigned long c;
  while((c=ressu_genlong()) >= (((unsigned long)0xffffffffffffffff)/limit)*limit);
  return(c%limit);
}

unsigned long ressu_gen_limit(unsigned long limit)
{
  if(limit<=256)
    return(ressu_genbyte_limit(limit));
  else if(limit<=65536)
    return(ressu_genshort_limit(limit));
  else if(limit<=(unsigned long)65536*65536)
    return(ressu_genint_limit(limit));
  else if(limit<=(unsigned long)0xffffffffffffffff)
    return(ressu_genlong_limit(limit));
  else
    return(-1);
}

#define MAIN 2

#ifdef MAIN

int help=0;

int utf8len(unsigned char *buf)
{
  int len;
  unsigned char *p;
  
  p=buf;
  len=0;
  while(*p!='\0') {
    if(*p<0x80 || // ascii char
       *p>0xbf) // first utf8 byte
      len++;
    p++;
  }
  return(len);
}

#define aDEBUG38 2

void utf8getchar(int size, unsigned char *buf, int n, unsigned char *string)
{
  int d;
  unsigned char *p,*q;
  
  d=0;
  p=string;
  q=buf;

  // find first byte of character
  
  while(*p!='\0') {
    if(*p<0x80 || // ascii char
       *p>0xbf) { // first utf8 char
      if(d==n)
	break;
      d++;
    }
    p++;
  }

  // copy first byte and rest
  // of character
  
  if(*p!='\0') {
    *q++=*p; // copy first byte
    if(*p>0xbf) { // if first is utf8 char
      p++;
      for(;;) {
	if(*p>0xbf || // first utf8 char
	   *p<0x80 || // ascii char
	   *p=='\0')  // end of file
	  break;
	*q++=*p++; // copy rest of the bytes
      }
    }
  }
  *q='\0';

#ifdef DEBUG38
  fprintf(stdout,"%s: utf8getchar:",procname);
  fprintf(stdout," string: %s",string);
  fprintf(stdout,", n: %d",n);
  fprintf(stdout,", character: %s",buf);
  fprintf(stdout,"\n");
#endif
}

#define aDEBUG45

void out_word(int size, unsigned char *buf, unsigned char *digits, unsigned long word2) // 8.5.2021 JariK
{
  int c, d, len, digitslen;
  unsigned long word;
  unsigned char string[132], character[32];

  digitslen = utf8len(digits);
  word=word2;
  len=0;
  string[0]='\0';

  if(word==0 || digitslen<2) {
    // zero
    utf8getchar(sizeof(character),character,0,digits);
    if(len+strlen(character)<sizeof(string)) {
      strcat(string,character);
      len+=strlen(character);
    }
  } else {
    // non zero
    while(word>0) {
      utf8getchar(sizeof(character),character,word%digitslen,digits);
      if(len+strlen(character)<sizeof(string)) {
	strcat(string,character);
	len+=strlen(character);
      }
      word/=digitslen;
    }
  }

  // reverse string
  
  *buf='\0';
  len=0;
  d=utf8len(string);
  for(c=d-1;c>=0;c--) {
    utf8getchar(sizeof(character),character,c,string);
    if(len+strlen(character)<size) {
      strcat(buf,character);
      len+=strlen(character);
    }
  }

#ifdef DEBUG45
  fprintf(stdout,"]\n%s: out_word: ",procname);
  fprintf(stdout," reverse string: %s",string);
  fprintf(stdout,", digits: %s",digits);
  fprintf(stdout,", int: %lu",word2);
  fprintf(stdout,"\n[");
#endif
}

#define aDEBUG58 2

void in_word(unsigned long *word, unsigned char *digits, unsigned char *buf)
{
  int c,d,e,f,ok;
  unsigned char character[32], character2[32];

  *word=0;
  d=utf8len(buf);
  f=utf8len(digits);

  for(c=0;c<d;c++) {
    utf8getchar(sizeof(character2),character2,c,buf);
    ok=0;
    for(e=0;e<f;e++) {
      utf8getchar(sizeof(character),character,e,digits);
      if(!strcmp(character,character2)) {
	ok=1;
	break;
      }
    }
    if(ok) {
      *word=((*word)*f)+e;
    } else {
      fprintf(stdout,"%s: in_word:",procname);
      fprintf(stdout," illegal digit '%s'\n",character2);
      help=1;
    }
  }
#ifdef DEBUG58
  fprintf(stdout,"%s: in_word:",procname);
  fprintf(stdout," word: %lu",*word);
  fprintf(stdout,", digits: %s",digits);
  fprintf(stdout,", string: %s",buf);
  fprintf(stdout,"\n");
#endif
}

void line_clear(int *len, unsigned char **buf)
{
  if(*len<1) {
    *len=129;
    *buf=realloc(*buf,*len);
  }
  **buf='\0';
}

int line_add_string_sort(int *len, unsigned char **buf, unsigned char *string)
{
  int cmp,count,add;
  unsigned char *p;

  p=*buf;
  add=1;
  while(*p!='\0') {
    cmp=strncmp(string,p,strlen(string));
    if(cmp>0) {
      while(*p!=' ' && *p!='\0') // find next space
	p++;
      while(*p==' ' && *p!='\0') // find next non space
	p++;
    } else if(cmp==0) {
      add=0;
      break;
    } else {
      break;
    }
  }

  if(add) {
    count=strlen(*buf)+strlen(string)+1+1;
    if(*buf==NULL || count>*len) {
      int diff=p-*buf;
      *len = *len + 128;
      *buf=realloc(*buf,*len);
      p=*buf+diff;
    }
    memmove(p+strlen(string)+1,p,strlen(p)+1);
    memmove(p,string,strlen(string));
    p+=strlen(string);
    *p=' ';
  }

  return(add);
}

void line_get_string(int len, unsigned char *buf, int n, unsigned char *string)
{
  int e,ok,count;
  unsigned char *p,*q;

  ok=0;
  p=string;
  e=0;

  while(*p!='\0') {
    if(e==n) {
      ok=1;
      break;
    }
    while(*p!=' ' && *p!='\0') // find next space
      p++;
    while(*p==' ' && *p!='\0') // find next non space
      p++;
    e++;
  }
  if(ok) {
    q=buf;
    count=0;
    while(*q!=' ' && *q!='\0') {
      if(++count<len)
	*q++=*p;
      p++;
    }
    *q='\0';
  } else {
    buf[0]='\0';
  }
}

void line_dump(unsigned char *string)
{
  int e,n;
  unsigned char *p;

  p=string;
  n=0;
  while(*p!='\0') {
    fprintf(stdout," %d:",n++);
    while(*p!=' ' && *p!='\0') // find next space
      putchar(*p++);
    while(*p==' ' && *p!='\0') // find next non space
      p++;
    e++;
  }
  fprintf(stdout,"\n");
}

static int size=5, zero=1, sspace=0, scrlf=1,
  chars=72, pchars=0, words=0, pwords=0,
  lines=10, plines=0, plinesdigits=5, slineno=1,
  quiet=0, sort=0, unique=0, sample=0;
static unsigned long limit, word;
static unsigned char *digits="0123456789", character[32];
static unsigned char digitstemp[256];
static unsigned char linenobuf[1024];

unsigned char wordbuf[1024];

void readword(unsigned char *buf)
{
  int d,e;
  unsigned char temp1024[1024];
  
  if(limit!=0) {
    word=0;
    
    if(zero) {
      word=ressu_gen_limit(limit); // include zeroes
      //fprintf(stdout,"(%02ld)",word);
    } else if(limit>=1) {
      while((word=ressu_gen_limit(limit))==0); // skip zeroes
    }
	  
    out_word(sizeof(temp1024),temp1024,digits,word);
	  
    // fill leading zeroes
    
    buf[0]='\0';
    utf8getchar(sizeof(character),character,0,digits);
    for(d=size-utf8len(temp1024);d>0;d--) {
      strcat(buf,character);
    }
	  
    // rest of the number
	  
    strcat(buf,temp1024);
	  
  } else if(digits!=NULL) {
    int digitslen;
	  
    buf[0]='\0';
    digitslen=utf8len(digits);
	  
    // fill whole word digit by digit
	  
    //fprintf(stdout,"[");
    for(d=0;d<size;d++) {
      if(digits[0]=='0' && !zero)
	e=ressu_gen_limit(digitslen-1)+1;
      else
	e=ressu_gen_limit(digitslen);
      utf8getchar(sizeof(temp1024),temp1024,e,digits);
      //fprintf(stdout,"%s",temp1024);
      strcat(buf,temp1024);
    }
    //fprintf(stdout,"]");
  }
}

int line_number_length()
{
  int c;
  out_word(sizeof(linenobuf),linenobuf,"0123456789",lines-1);
  c=strlen(linenobuf);
  if(c<5)
    c=5;
  return(c);
}

void print_line_number()
{
  unsigned char linenobuf[1024];
  
  if(pchars==0 && !quiet && slineno) {
    sprintf(linenobuf,"%0*d",plinesdigits,plines);
    fprintf(stdout,"%s",linenobuf);
    pchars +=strlen(linenobuf);
    fprintf(stdout," ");
    pchars++;
  }
}

void print_spaces()
{
  if(sspace && pchars>0 && !quiet) {
    if(sspace==3 && pwords%2==0) {
      fprintf(stdout," ");
      pchars++;
    }
    fprintf(stdout," ");
    pchars++;
  }
}

void print_word(unsigned char *buf)
{
  if(size!=0) {
    if(!quiet)
      fprintf(stdout,"%*s",size,buf);
    pchars += size;
    pwords++;
  } else {
    if(!quiet)
      fprintf(stdout,"%s",buf);
    pchars += strlen(wordbuf);
    pwords++;
  }
}

#define aDEBUG86 2
#define aDEBUG87 2
#define aDEBUG88 2
#define aDEBUG89 2

int main(int argc, char *argv[])
{
  int c,d;
 
  procname=argv[0];

  limit=0;
#ifdef DEBUG4
  ccp=cc;
  cc_bytes=0;
#endif
  clockbytes=0;
  for(d=0;d<1024;d++)
    periods[d]=0;

  // look thru command line parameters
  
  for(c=1;c<argc;c++) {
    if(!strncmp("-",argv[c],1)) {
      if(!strcmp("--lineno",argv[c])) {
	slineno=!slineno;

      } else if(!strcmp("--crlf",argv[c])) {
	scrlf=!scrlf;
	slineno=0;

      } else if(!strcmp("--quiet",argv[c])) {
	quiet=!quiet;

      } else if(!strcmp("--stats",argv[c]) ||
	!strcmp("--stat",argv[c])) {
	stats=!stats;

      } else if(!strcmp("--space",argv[c])) {
	sspace=!sspace;

      } else if(!strcmp("--zero",argv[c])) {
	zero=!zero;

      } else if(!strcmp("--sort",argv[c])) {
	sort=!sort;
	if(sspace==0)
	  sspace=1;
		
      } else if(!strcmp("--unique",argv[c])) {
	unique=!unique;
	if(sspace==0)
	  sspace=1;
		
      } else if(!strcmp("--lotto",argv[c])) {
	sort=!sort;
	if(sspace==0)
	  sspace=1;
	//sort=1;
	//unique=1;

      } else if(!strcmp("--sample",argv[c])) {
	sample=!sample;
	
      } else if(!strcmp("--copyright",argv[c]) ||
	 !strcmp("--version",argv[c])) {
	fprintf(stderr,"%s",programname);
	fprintf(stderr,", %s",copyright);
	fprintf(stderr,"\n\n");
	help=1;
	
      } else if(!strncmp("--lim",argv[c],5)) {
	if(*(argv[c]+5)!='\0') {
	  in_word(&limit, digits, argv[c]+5);
	} else if(c+1<argc) {
	  in_word(&limit, digits, argv[c+1]);
	  c++;
	}
	if(sspace<1) // 23.6.2021
	  sspace=1;

      } else if(!strncmp("-s",argv[c],2)) {
	if(*(argv[c]+2)!='\0') {
	  size=atoi(argv[c]+2);
	} else if(c+1<argc) {
	  size=atoi(argv[c+1]);
	  c++;
	}
	limit=0; // 23.6.2021
	if(size==0)
	  size=1;
	if(size>1024)
	  size=1024;
      } else if(!strncmp("--bits",argv[c],6)) {
	if(*(argv[c]+6)!='\0') {
	  ressu_bits_needed=atoi(argv[c]+6);
	} else if(c+1<argc) {
	  ressu_bits_needed=atoi(argv[c+1]);
	  c++;
	}	

      } else if(!strncmp("--bytes",argv[c],7)) {
	if(*(argv[c]+7)!='\0') {
	  ressut_bytes=atoi(argv[c]+7);
	} else if(c+1<argc) {
	  ressut_bytes=atoi(argv[c+1]);
	  c++;
	}	

      } else if(!strncmp("-w",argv[c],2)) { // words per line
	if(*(argv[c]+2)!='\0') {
	  words=atoi(argv[c]+2);
	} else if(c+1<argc) {
	  words=atoi(argv[c+1]);
	  c++;
	}
	if(words<1)
	  words=1;
	
	chars = 0;
	
      } else if(!strncmp("-c",argv[c],2)) {  // characters per line
	if(*(argv[c]+2)!='\0') {
	  chars=atoi(argv[c]+2);
	} else if(c+1<argc) {
	  chars=atoi(argv[c+1]);
	  c++;
	}
	words=0;
	
      } else if(!strncmp("-l",argv[c],2)) { // lines
	if(*(argv[c]+2)!='\0') {
	  lines=atoi(argv[c]+2);
	} else if(c+1<argc) {
	  lines=atoi(argv[c+1]);
	  c++;
	}
	if(lines<1)
	  lines=1;

      } else if(!strcmp("-x",argv[c])) {
	digits = "0123456789abcdef";
	size=4;

      } else if(!strcmp("-d",argv[c])) {
	digits = "0123456789";
	size=5;

      } else if(!strcmp("-o",argv[c])) {
	digits = "01234567";
	size=3;

      } else if(!strcmp("-b",argv[c])) {
	digits = "01";
	size=8;

      } else if(!strcmp("-1",argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	size=8;

      } else if(!strcmp("-2",argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz" \
	  "_-";
	size=8;

      } else if(!strcmp("-3",argv[c])) { // 9.5.2021 JariK
	digits=
	  "!\"#$%&'()*+,-./0123456789:;<=>?@" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" \
	  "abcdefghijklmnopqrstuvwxyz{|}~";
	size=8;

      } else if(!strcmp("-4",argv[c])) {
	digits=
	  "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ" \
	  "абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	size=8;

      } else if(!strcmp("--isalnum",argv[c]) || // 9.5.2021 JariK
	        !strcmp("--isalpha",argv[c]) ||
	        !strcmp("--isdigit",argv[c]) ||
	        !strcmp("--isgraph",argv[c]) ||
	        !strcmp("--islower",argv[c]) ||
	        !strcmp("--isprint",argv[c]) ||
	        !strcmp("--ispunct",argv[c]) ||
	        !strcmp("--isupper",argv[c]) ||
		!strcmp("--isxdigit",argv[c])) {
	unsigned char *p=digitstemp;
	for(d=0;d<256;d++) {
	  if((!strcmp("--isalnum",argv[c]) && isalnum(d)) ||
	     (!strcmp("--isalpha",argv[c]) && isalpha(d)) ||
	     (!strcmp("--isdigit",argv[c]) && isdigit(d)) ||
	     (!strcmp("--isgraph",argv[c]) && isgraph(d)) ||
	     (!strcmp("--islower",argv[c]) && islower(d)) ||
	     (!strcmp("--isprint",argv[c]) && isprint(d)) ||
	     (!strcmp("--ispunct",argv[c]) && ispunct(d)) ||
	     (!strcmp("--isupper",argv[c]) && isupper(d)) ||
	     (!strcmp("--isxdigit",argv[c]) && isxdigit(d))) {
	    *p++=d;
	  }
	}
	*p='\0';
	digits=digitstemp;
	size=8;

      } else if(!strcmp("--rand",argv[c])) {
	digits = "0123456789";
	sspace=3;
	slineno=1;
	words=10;
	chars=0;
	limit=100000;
	//size=5;
	//lines=20000;
	
      } else if(!strncmp("-i",argv[c],2)) {
	digits=NULL;
	if(*(argv[c]+2)!='\0') {
	  digits=argv[c]+2;
	} else if(c+1 < argc) {
	  digits=argv[c+1];
	  c++;
	}
	if(digits==NULL || utf8len(digits)<2) {
	  fprintf(stderr,"%s: not enough digits \"%s\"\n",procname,argv[c]);
	  help = 1;
	}
	size=1;
      } else if(!strcmp("--ressu",argv[c])) {
	input=0;
      } else if(!strcmp("--debug",argv[c])) {
	input=1;
      } else if(!strcmp("--fast",argv[c])) {
	input=2;
      } else if(!strcmp("--single",argv[c])) {
	input=3;
      } else if(!strcmp("--urandom",argv[c])) {
	input=8;
#ifdef USE_RANDOM
      } else if(!strcmp("--random",argv[c])) {
	input=9;
#endif
      } else {
	fprintf(stderr,"%s: invalid option %s\n",procname,argv[c]);
	help = 1;
      }
    } else {
      help = 1;
    } // if(!strncmp
  } // for(c=0

  if(sample) {
    FILE *fp1;
    unsigned char buffer[4096];

    fp1=fopen("newressusample.rnd","w");
    for(c=0;c<1048576;c++) {
      ressu_genbytes(sizeof(buffer),buffer);
      fwrite(buffer,1,sizeof(buffer),fp1);
      ressu_genbytes(sizeof(buffer),buffer);
      fwrite(buffer,1,sizeof(buffer),fp1);
    }
    fclose(fp1);
    exit(0);
  }
  // get linenumber length in digits

  plinesdigits=line_number_length();

  // get data length in digits

  if(limit!=0) {
    unsigned char wordbuf[128];
    out_word(sizeof(wordbuf),wordbuf,digits,limit-1);
    size=utf8len(wordbuf);
  }

  pchars=0;
  pwords=0;
  int linew=0;

  // in beginning of line, count printed line number
      
  if(!quiet && slineno) {
    sprintf(linenobuf,"%0*d",plinesdigits,plines);
    pchars += strlen(linenobuf);
    pchars++;
  }

  // count words
  
  while((chars>0 && pchars+size < chars) ||
	(words>0 && pwords < words)) {

    linew++;
    
    if(sspace && pchars>0 && !quiet) {
      if(sspace==3 && pwords%2==0)
	pchars++;
      pchars++;
    }
	
    pchars+=size;
    pwords++;
  }

  unsigned long wordvalues=1;
  for(c=0;c<size;c++) {
    if(wordvalues!=0 && wordvalues*utf8len(digits)<(unsigned long)65536*65536*65536) {
      wordvalues*=utf8len(digits);
    } else {
      wordvalues=0;
    }
  }

  if(limit > 0) {
    if(sort==1 && limit-!zero<words) {
      fprintf(stderr,"%s: limit is less than words (zero)",procname);
      fprintf(stderr,", limit:%lu",limit);
      fprintf(stderr,", words:%d",words);
      fprintf(stderr,", zero:%d",zero);
      fprintf(stderr,"\n");
      
      help=1;
      lines=0;
    }
  } else {
    if(sort==1 && size>0 && wordvalues>0 && wordvalues<linew) {
      fprintf(stderr,"%s:",procname);
      fprintf(stderr," digits is less than calculated words");
      fprintf(stderr,", digits:%d(%lu)",utf8len(digits),wordvalues);
      fprintf(stderr,", words:%d\n",linew);
      help=1;
      lines=0;
    }
  }

  // small words as single ressu_gen_limit()
  
  if(limit==0 && wordvalues>0)
    limit=wordvalues;

  if(stats) {
    fprintf(stdout,"randomsource: %s",randomgen[input]);
    fprintf(stdout,", size: %d",size);
    fprintf(stdout,", linew: %d",linew);
    fprintf(stdout,", pchars: %d",pchars);
    fprintf(stdout,", pwords: %d",pwords);
    if(wordvalues>0)
      fprintf(stdout,", wordvalues: %lu",wordvalues);
    if(limit>0)
      fprintf(stdout,", limit: %lu",limit);
    fprintf(stdout,"\n");
  }
  
  if(help) {
    struct helpline {
      char *command;
      char *text;
    } helplines[] = {
      { "newressu -d", "print random decimal digits" },
      { "newressu -o", "print octal digits" },
      { "newressu -x", "print hexadecimal digits" },
      { "newressu -b", "print binary digits" },
      { "newressu -d --space", "print decimal digits, with spaces between \"words\"" },
      { "newressu -b --space", "print binary digits, with spaces between \"words\"" },
      { "newressu -d -l30", "print decimal digits, 30 lines" },
      { "newressu -d -l30 --lineno", "print decimal digits, 30 lines, no linenumbers"  },
      { "newressu -d -c128", "print decimal digits, 128 characters per line"  },
      { "newressu -d --lim10", "print decimal numbers between 0-9" },
      { "newressu -d --lim10 -x","print hexadecimal numbers with decimal limit" },
      { "newressu -d -s10 --space","print decimal numbers with spaces and wordsize 10" },
      { "newressu -d --space --zero --lim7","print rollings of dice (1-6)" },
      { "newressu -b -s1 --space","print throws of coin (0,1)" },
      { "newressu -d --lim41 -w7 --zero --lotto","print lotto numbers for finnish lotto 7x(1-40)" },
      { "newressu -d -s5 --sort -w16","print 16 sorted decimal numbers from 0 to 99999" },
      { "newressu --isalnum", "print alphanumeric characters" },
      { "newressu --isalpha", "print alphabetic characters" },
      { "newressu --isgraph", "print printables excluding space" },
      { "newressu --islower", "print lowercase characters" },
      { "newressu --ispunct", "print punctuation characters" },
      { "newressu --isupper", "print uppercase characters" },
      { "newressu -1", "print material for passwords" },
      { "newressu -iаэыуояеёюи", "print russian vowels" },
      { "newressu --ressu", "use randomness from ressu (default)" },
      { "newressu --debug", "use randomness from debugging ressu" },
      { "newressu --fast", "use randomness from fast ressu" },
      { "newressu --single", "use randomness from single round of ressu" },
      { "newressu --urandom", "use randomness from /dev/urandom" },
#ifdef USE_RANDOM
      { "newressu --random", "use randomness from /dev/random" },
#endif
    };
    
    for(c=0;c<sizeof(helplines)/sizeof(helplines[0]);c++) {
      fprintf(stderr,"%-50s",helplines[c].text);
      fprintf(stderr,"%-25s",helplines[c].command);
      fprintf(stderr,"\n");
    }
    for(c=0;c<chars;c++)
      fprintf(stdout,"*");
    fprintf(stdout,"\n");
    if(lines>1)
      lines=1; // print one line below help as a sample
  }

  if(lines==0)
    exit(1);

  int linecnt=0;
  unsigned char *line=NULL;
  unsigned char wordbuf[1025];
  
  for(;;) {

    if(!sort) { // also lotto

      pchars=0;
      pwords=0;

      while(pwords<linew) {
	readword(wordbuf);

	// in beginning of line, print line number

	print_line_number();
	
	// want to print spaces between "words"?

	print_spaces();
	
	// print word

	print_word(wordbuf);

      } // while(pwords<linew) {
    } else { // if(!sort)

      pchars=0;
      pwords=0;
      
      line_clear(&linecnt,&line);

      // fetch and save words on this row
      while(pwords<linew) {
	readword(wordbuf);

	if(line_add_string_sort(&linecnt,&line,wordbuf)) {
	  pwords++;
	}
      } // while(pwords<linew) {

      pchars=0;
      pwords=0;

      // print words on this row
      while(pwords<linew) {

	line_get_string(sizeof(wordbuf),wordbuf,pwords,line);

	// in beginning of line, print line number

	print_line_number();
	
	// want to print spaces between "words"?
	
	print_spaces();

	// print word
	
	print_word(wordbuf);

      } // while(pwords<linew) {
    } // if(!sort)

    // line full?
    
    if(!quiet) {
      fprintf(stdout,"\n");
    }
    plines++;
    if(sspace==3 && plines<lines && plines%5==0)
      fprintf(stdout,"\n");

    // all needed lines printed?

    if(plines >= lines)
      break;

  } // for(;;)
  fflush(stdout);
}

#endif // MAIN

Published
Categorized as ressu