Kaikki oikeudet pidätetään. Tässä ressun uusimmassa versiossa on uusittu asiakaspuskurin hallintaa, löydetty kellojonosta löytyvien satunnaisbittien “laskentaan” uusi menetelmä.
Asiakaspuskurin hallinnassa aiemmin asiakas toimitti puskurin satunnaisbittigeneraattorin täytettäväksi, ja satunnaislukugeneraattorilla ei ollut “omaa” muistialuetta. Näin jää aina mietittäväksi voisiko edellisestä puskurista laskea seuraavan tai päätellä satunnaisbittigeneraattorin ensimmäisen puskurin. Uudessa versiossa ressulla on oma puskurinsa, josta ressu palauttaa satunnaisia merkkejä asiakaspuskuriin. Näin asiakas ei saa koko puskurin rakennetta, vaan satunnaisen osan siitä.
Kellojonosta löytyvien satunnaisbittien laskentaa on useassa aiemmassa postissa, ja nyt on tästä uusin versio ajattelun alla. Uusi versio perustuu fluctuations eli vaihtelut tulosteeseen. Tulosteesta poistetaan suurimmat ketjut ja jäljelle jäävät lasketaan yhteen. Tästä saadaan arvioidut satunnaisbitit.
Ajatuksena on että nämä suurimmat ketjut ovat se perusmateriaali jota kellomateriaali sisältää ja että ne eivät sisältäisi paljoa satunnaisuutta. Toisaalta nämä pienet ketjut esiintyvät silloin tällöin satunnaisessa paikassa koko kellomateriaalin alueella ja näin niistä voidaan aina laskea jonkin verran satunnaisuutta.
Lisäksi lopussa on kiva lelu satunnaismerkkien hakemiseen.
Edit: korjailtu bugeja ja ohjelman formatointia. Nyt täytyy sanoa, että tämä on mielestäni lähimpänä tuotantoversiota tähän asti. Ei vieläkään ainoana generaattorina vaan lisättynä johonkin/joihinkin muuhun generaattoriin. Jatkan tutkimista..
Edit: muutettu lim lauseen sqrt cbrt:ksi. Kokeilu.
Tässä postissa käyn uusimman ohjelmaversion läpi, ja kommentoin kappaleita. Vaihtelut lasketaan periods tauluun ja clockbytes on kellojonon merkkien lukumäärä. Kuvassa oleva ressu_clockbyte() rutiini on vanha tuttu millisekuntien alimman merkin palauttava rutiini:
unsigned long periods[1024];
unsigned long genbytes=0;
unsigned long clockbytes=0;
#define DEBUG8 2
unsigned char ressu_clockbyte() /* JariK 2013 */
{
struct timeval tv;
gettimeofday(&tv, NULL);
return(tv.tv_usec & 0xff);
}
Seuraava rutiini on ressun varsinainen generaattoripätkä, joka hakee varsinaiset satunnaisluvut. Se lukee kellojonoa ja xor:aa kellojonon merkillä puskurissa olevan merkin. Ensin xora:taan koko jonosta alin bitti, sitten toiseksi alin bitti, kolmanneksi alin ja niin edelleen. Aina kun koko puskurin yksittäinen bitti on xor:attu vaihdetaan puskurissa merkkien paikkoja ja vaihdetaan alinta bittiä. Puhun muuten alimmasta bitistä, mutta todellisuudessa koko puskurin merkki xorataan. Tärkeätä on se, että kellojonon alimmalla bitillä xorataan kaikki puskurin bitit. Alin bitti on se, jossa on eniten vaihtelua. Lisäksi puskurin kaikki bitit kaikkia muitakin kellojonon 8 alinta bittiä.
Seuraavan kappaleen ensimmäinen luuppi (for(d=0)) tekee xor:auksen yhdelle bitille (koko puskurissa). Toinen (for(d=0)) luuppi vaihtaa kaikkien merkkien paikkoja keskenään. Näitä kahta suoritetaan 8 kertaa (for(c=0)), yksi jokaiselle merkkien bitille.
Yhden genbytes_fast rutiinin ajon aikana yksittäinen merkki xor:ataan kahdeksan kellojonon bitin kanssa, ja kellojonon bitti vaihtelee merkin puskuripaikan mukaan. Puskuripaikka vaihtelee satunnaisesti.
Aliohjelmassa on kolme erilaista merkin pyöritysvaihtoehtoa, jotka ovat mahdollisia. Näistä on kuitenkin käytetty vain rotate left 1 vaihtoehtoa. Rotate left 3 kattaisi teoriassa nopeammin koko merkin, mutta sitä ei ole tosiaan käytetty.
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;
}
}
}
Seuraava rutiini on varsinainen asiakkaan kutsuma rutiini, kuten sanottu tässä versiossa pidetään varsinaiset satunnaismerkit ja asiakkaan puskuri erillään. Asiakkaan puskuriin merkit kootaan (for(c=0)) luupilla, ja jos varsinaiset satunnaisbitit loppuvat (if ressut_pos) ehtolauseella haetaan niitä lisää. DEBUG9 ja DEBUG8 ovat debukkailua varten, ja ensimmäisellä yritetään synkronoida kellojonot siten että satunnaisbitit poimitaan samanlaisesta kellojonon kohdasta debukkailua varten, toisella taas raportoidaan debug tiedostoon “newressu.deb” juuri haettu puskuri. Tiedosto esittelee parhaiten sen miten teoreettiset satunnaisbitit lasketaan. Siitä lisää kuvan jälkeen:
Lisäksi rutiinissa on uutena asiana se että, miten paljon satunnaisbittejä tuotetaan ja miten paljon “teoreettisia bittejä” tarvitaan määritellään erikseen. Aiemmissa versioissa tuotettavat ja laskennalliset satunnaisbitit olivat sama asia. Ne määritellään alun #defineissä.
#define RESSUT_SIZE 4096
#define RESSU_BITS_NEEDED 128
#define RESSU_MIN_ROUNDS 2
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_size = RESSUT_SIZE,
ressut_f = 0;
static unsigned char ressut[RESSUT_SIZE];
for(c=0; c<size; c++) {
if(ressut_pos == 0) {
if(ressut_first) {
memset(ressut,0,sizeof(ressut));
ressut_first=0;
}
clockbytes=0;
for(d=0;d<1024;d++)
periods[d]=0;
#ifdef DEBUG9
while(ressu_clockbyte()!=0);
while(ressu_clockbyte()==0);
#endif
int rndbits=0, rounds=0;
for(d=0; rndbits<RESSU_BITS_NEEDED ||
d<RESSU_MIN_ROUNDS; d++) {
rounds++;
ressu_genbytes_fast(sizeof(ressut),ressut);
rndbits=0;
for(e=0;e<1024;e++) {
if(periods[e]>0 && periods[e]<sqrt((double)clockbytes)) {
rndbits+=periods[e];
}
}
}
#ifdef DEBUG8
FILE *fp1;
if((fp1=fopen("newressu.deb","a"))!=NULL) {
for(d=0;d<32;d++)
fprintf(fp1,"%02x",ressut[d]);
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,", limit: %lu",(unsigned long)sqrt((double)clockbytes));
fprintf(fp1,", skipped:");
for(d=0;d<1024;d++) {
if(periods[d]>=sqrt((double)clockbytes))
fprintf(fp1," %d:%lu",d,periods[d]);
}
fprintf(fp1,", counted:");
rndbits=0;
for(d=0;d<1024;d++) {
if(periods[d]>0 && periods[d]<sqrt((double)clockbytes)) {
fprintf(fp1," %d:%lu",d,periods[d]);
rndbits+=periods[d];
}
}
fprintf(fp1,", rndbits: %d",rndbits);
fprintf(fp1,", rounds: %d",rounds);
fprintf(fp1,"\n");
fclose(fp1);
} // if((fp1=fopen
#endif
} // if(ressut_pos == 0)
ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_size;
buffer[c] ^= ressut[ressut_f];
ressut_pos = (ressut_pos+1) % ressut_size;
} // for(c=0; c<size; c++)
genbytes+=size;
}
Seuraavassa kappaleessa on esimerkki newressu.deb tiedostoon kerätystä tietueesta: clockbytes kertoo tällä hetkellä käytettyjen kellomerkkien määrän, limit kenttä lasketaan clockbytes kentästä (limit=sqrt(clockbytes)), fluctuations kertoo tässä genbytes_fast ajossa olleet vaihtelut, skipped kertoo laskennassa ohitetut vaihtelut (>=limit), counted kertoo teoreettisiin satunnaisbitteihin lasketut kellomerkit, rndbits on counted kenttien summa, suoritetut kierrokset tällä ajokerralla on 8. ressu_genbytes_fast:ia ajetaan niin kauan kun teoreettisia satunnaisbittejä on tarvittava määrä.
9242aeaa253e187ed893374299d3c40a9ebcc4372f615269533f69fa163345b5, clockbytes: 262136, fluctuations: 1:13 2:7 3:15 4:12 5:13 6:8 7:9 8:9 9:6 10:11 11:10 12:8 13:22 14:9308 15:8720, total: 262136, limit: 511, skipped: 14:9308 15:8720, counted: 1:13 2:7 3:15 4:12 5:13 6:8 7:9 8:9 9:6 10:11 11:10 12:8 13:22, rndbits: 143, rounds: 8
Seuraavassa vanhat tutut aputoiminnot, jotka käyttävät ressu_genbytes funktiota: rutiinit merkin (8 bits), shortin (16 bits), intin (32 bits) ja longin(64 bits) arpomiseksi. _limit-loppuisissa on yläraja halutulle satunnaisnumerolle. Uutena näissä on ressu_gen_limit, joka valitsee parametrina annetun limit:in mukaan sopivan talletuskoon.
int ressu_genbyte()
{
unsigned char ch;
ressu_genbytes(sizeof(ch), &ch);
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);
}
Loppuosuus postista koskee lelusovellusta, eli ensin lyhyt esittely ja sitten lähdekielinen versio. Näissä esimerkeissä rivin pituus on 72:n sijaan 52, jotta ne mahtuvat koodi-ikkunaan.
Aluksi virheellisistä komennoista tulostetaan käyttöohje:
./newressu -?
./newressu: invalid option -?
print random decimal digits newressu -d
print octal digits newressu -o
print hexadecimal digits newressu -x
print binary digits newressu -b
print decimal digits, with spaces between "words" newressu -d --space
print binary digits, with spaces between "words" newressu -b --space
print decimal digits, 30 lines newressu -d -l30
print decimal digits, 30 lines, no linenumbers newressu -d -l30 --lineno
print decimal digits, 128 characters per line newressu -d -c128
print decimal numbers between 0-9 newressu -d --lim10
print hexadecimal numbers with decimal limit newressu -d --lim10 -x
print decimal numbers with spaces and wordsize 10 newressu -d -s10 --space
print alphanumeric characters newressu --isalnum
print alphabetic characters newressu --isalpha
print digits newressu --isdigit
print printables excluding space newressu --isgraph
print lowercase characters newressu --islower
print printable characters including space newressu --isprint
print punctuation characters newressu --ispunct
print uppercase characters newressu --isupper
print hexadecimal digits newressu --isxdigit
print material for passwords newressu -1
print random vowels newressu -iaeiouyåäö
print russian vowels newressu -iаэыуояеёюи
flags newressu -s -i
print nice labyrinth newressu -i "/\"
print statistics for ressu run newressu --stats
print rand style random number table newressu --rand -l20000
Seuraavassa komento ilman parametreja: komento tulostaa kymmenen riviä desimaalisatunnaislukuja.
jarik>newressu
00000 056552593701856488743208064053594455181555491
00001 647836083655640471912091465183887333363812520
00002 735962951043115775872773766654518963151341269
00003 603704844650943255437410070844585966628674315
00004 784156275041999237378412103711914182300000429
00005 780768799139985291313622649468657009981882664
00006 154427558713802757114262129440213749317145956
00007 137569491937026029405363878424500216776474105
00008 464074752787671526189691150232812511113631660
00009 640527175186115479581523659194559928382662503
Seuraavassa desimaalilukuja välilyönneillä: sanojen oletus pituus desimaaliluvuissa on 5.
>newressu --space
00000 61911 65224 24982 56022 53474 17097 92678
00001 91134 70560 25368 03967 41941 61026 68630
00002 87228 04142 57313 98460 67128 78125 17012
00003 59839 48032 77902 41734 64771 08145 89935
00004 68054 22423 12768 13571 47189 03983 59352
00005 78556 93369 17027 81312 98335 46255 01731
00006 04806 08563 34406 64229 09668 04101 23024
00007 55138 96983 11368 53354 74530 79906 56703
00008 20257 80030 54596 50211 81565 96167 57735
00009 46253 46243 60624 95624 85943 36918 62094
Seuraavassa kolmen merkin pituisia desimaalilukuja:
./newressu --space -s3
00000 184 476 605 450 056 680 522 882 987 770 157
00001 687 753 277 923 892 899 457 807 643 724 719
00002 862 773 931 034 740 401 054 207 909 158 466
00003 915 408 471 515 394 293 693 116 611 041 848
00004 824 829 778 991 875 242 931 270 555 233 375
00005 406 202 690 824 543 189 251 940 188 002 029
00006 216 377 415 765 202 231 355 597 366 998 530
00007 417 258 076 161 729 498 955 049 986 685 665
00008 794 585 407 603 007 283 829 118 759 769 789
00009 288 415 404 584 035 638 508 059 272 102 901
Seuraavassa desimaalisia satunnaislukuja välillä 0-11
./newressu --lim12
00000 00 03 02 01 05 04 06 08 09 00 08 00 05 02 06
00001 03 01 09 08 10 00 03 10 11 04 04 01 03 11 00
00002 11 06 07 03 02 01 06 09 05 11 09 10 11 02 01
00003 06 04 02 10 01 04 04 04 00 08 05 05 10 08 07
00004 01 07 04 00 05 00 03 00 06 07 06 10 06 05 08
00005 03 04 06 05 04 01 01 00 06 06 09 02 01 01 05
00006 10 01 10 08 05 07 07 04 05 03 04 00 00 09 07
00007 00 07 11 04 06 00 00 02 01 07 00 11 07 09 01
00008 00 08 05 08 11 06 11 04 06 03 10 04 01 11 01
00009 01 00 10 10 11 04 01 08 04 05 06 06 01 03 02
Edellinen ilman nollia:
./newressu --lim12 --zero
00000 01 02 07 05 07 11 07 05 11 07 08 11 01 11 08
00001 07 01 06 06 06 04 09 08 10 08 06 02 03 10 03
00002 08 10 01 10 07 06 10 01 03 08 08 08 04 07 07
00003 04 05 02 05 04 03 04 09 06 03 09 09 02 07 03
00004 10 01 07 05 05 07 03 07 08 01 02 02 05 10 11
00005 07 10 09 09 09 10 02 11 01 02 04 03 08 08 02
00006 02 11 08 08 05 01 09 04 08 04 06 09 05 09 02
00007 03 01 10 07 06 10 07 06 07 10 02 06 02 06 06
00008 06 04 05 03 05 05 01 04 09 03 04 05 02 05 11
00009 04 06 05 07 04 01 03 11 10 02 04 10 06 06 05
Edellinen ilman rivinumeroita
./newressu --lim12 --zero --lineno
07 06 02 11 03 10 07 09 01 08 10 03 10 07 11 05 07
05 07 04 06 01 07 02 11 07 06 06 10 11 09 07 08 09
01 11 09 05 11 06 03 01 02 01 07 08 03 05 07 10 09
11 02 08 06 04 07 02 04 03 07 09 06 04 02 05 11 07
06 07 05 08 05 08 04 02 04 05 10 01 11 10 08 08 11
01 08 06 11 03 10 06 05 02 10 09 06 04 01 04 04 08
09 03 02 11 11 04 08 01 11 03 08 11 11 08 07 07 01
10 06 02 10 11 08 03 01 09 03 03 02 11 03 03 09 05
09 11 03 04 03 03 06 01 09 03 03 06 09 07 07 07 06
08 10 08 03 03 08 06 01 10 04 09 04 08 02 10 02 07
Seuraavassa heksadesimaalilukuja, näihin voit tietysti käyttää myös muita edellisiä parametreja.
./newressu -x
00000 fbcfbd8a4a099ec66469ee09262455592c969430da55
00001 7d1310727a2bcdf31bce90cc12b064159c09c1db6bd0
00002 c60de6a1cc10cbcadc158ed847e7e46e5ff5f7cbc846
00003 bd6f59d206f842f6049fe069859c16466eb06a84b911
00004 dbad7e8ef05e7a601030396239aee557a8b73cfd1fe0
00005 dfaf1bb7931cba2384de4b65be3813d213e0fdb737e2
00006 f31a950b4164660af6b869ba2155e85db025d515a84a
00007 a1d5acd029b4d59bf306c1376ea09d0dcd99677b7689
00008 53c30e8edb0c6eba4da665a2d22c979a84304f49d243
00009 74aa84093433e3421c25aafd55a9b2e9111ed01bff96
Tulostetaan heksalukuja vain 5 riviä:
./newressu -x -l5
00000 cc47a24ad9e2e9b12e9c780304d860b0f2b9b81e08d6
00001 aeeeac102c5759295cc8de62fa3470748ec4b85ffa1c
00002 6b1aa5da4761932b02f396db5a4b0a67c82d1d973a2e
00003 1e42d547a1e3dd475b40ec5b762531ad44f2495cca0d
00004 efbe4cda2f3f434aa5be5bf5bfc7055ef1b3e38e39dd
Seuraavassa potentiaalisia lottonumeroita: (muista tippi jos voitat..)
./newressu --lim41 --zero
00000 03 29 40 16 06 21 19 25 30 02 34 20 35 18 05
00001 15 30 10 24 14 40 11 26 38 37 07 01 01 34 32
00002 09 23 10 26 27 16 14 17 40 04 04 06 21 29 30
00003 35 33 26 30 03 04 25 03 08 39 18 01 02 36 18
00004 05 30 37 10 35 06 01 07 25 02 33 05 27 01 39
00005 38 25 35 08 03 12 26 04 10 12 26 09 11 12 35
00006 21 31 08 11 08 10 13 36 38 05 07 01 18 08 30
00007 15 33 09 16 31 14 13 13 26 35 01 16 15 30 25
00008 03 14 31 20 05 21 16 28 36 09 26 20 19 36 05
00009 04 10 31 08 22 04 24 13 14 39 25 38 19 34 22
Seuraavassa vielä tuloste rand tyylisestä satunnaislukutaulukosta. Tämä ei mahdu kokonaan ikkunaan, loput sarakkeet on scrollattava esiin:
./newressu --rand
00000 82577 42188 38567 42595 40111 51644 43968 18747 74456 07646
00001 10773 43080 85405 81270 17432 97488 78817 56028 85562 72060
00002 06644 40581 38948 13292 56989 49852 50198 22957 44732 54797
00003 18889 86978 93590 17269 79162 84652 54330 67367 76106 75227
00004 77478 81138 14027 44831 73289 58068 70203 63984 98229 81232
00005 73099 63728 41327 51316 74366 74384 95033 42243 11485 90571
00006 08640 61727 17171 35590 46059 81295 20169 66542 31996 07807
00007 17946 54483 37618 77037 64233 74302 28806 68128 39779 09417
00008 39456 17072 01615 32469 88133 14096 34895 95089 47737 25905
00009 71238 89283 94066 61245 18258 93718 77054 79450 99920 86500
Loput rutiinit on lopun lelusovellusta varten. Tämä ensimmäinen laskee utf8 merkkijonossa olevien merkkien määrin:
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);
}
Seuraavalla voidaan kopioida haluttu merkki tai utf8 merkin merkkijono string jonosta buf jonoon. Merkin numero annetaan n muuttujalla:
#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
}
Seuraava out_word rutiini tulostaa word2 muuttujan luvun buf merkkijonoon. Konversiossa käytetään digits merkkijonoa, jonka pitäisi sisältää halutun lukujärjestelmän luvut 0:sta loppuun asti, esimerkiksi desimaali merkkijono on “0123456789”, heksadesimaali on “0123456789abcdef”, binääri taas “01” ja oktaali “01234567”.
#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], *p;
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
}
Seuraava rutiini tekee tulostuksen toisinpäin, eli word-muuttujaan tulee buf kentästä saatavan merkkijonon arvo. Konversiossa käytetään taas digits merkkijonossa olevaa lukusarjan numeroiden luetteloa:
#define DEBUG58 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], *p, *q;
*word=0;
p=buf;
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
}
Lopuksi satunnaislukulelun pääohjelman valittuja palasia. Pääohjelma on postin lopussa kokonaisuudessaan. size muuttujassa on “sanan” eli numeron pituus merkkeinä. Esimerkiksi 10000:n pituus on 5 merkkiä. sspace kertoo tulostetaanko numeroiden väliin välilyönnit. (0=ei tulosteta,1=tulostetaan). chars kertoo, kuinka monta merkkiä mahtuu riville. pchars kertoo kuinka monta merkkiä riville on tällähetkellä tulostettu. words kertoo kuinka monta sanaa mahtuu riville. pwords kertoo kuinka monta sanaa on jo tulostettu. lines on tulostettavien rivien lukumäärä. plines on tulostettujen rivien lukumäärä. quiet on hiljainen tulostus, zero kertoo tulostetaanko nollat vai ei. Edelliset ovat oletusarvoja, komentorivin kytkimillä niille voidaan antaa uusia arvoja.
#define aDEBUG86 2
#define aDEBUG87 2
#define aDEBUG88 2
#define aDEBUG89 2
void main(int argc, char *argv[])
{
int c, d, bytes=1024, size=5, sspace=0,
chars=72, pchars=0, words=0, pwords=0,
lines=10, plines=0, plinesdigits=5, slines=1,
stats=0, quiet=0, zero=1;
unsigned char *p, buffer[32], *digits="0123456789",
digitstemp[256], character[32];
unsigned long limit;
procname=argv[0];
Seuraavat debukit kannattaa ehkä jättää päälle (86,87,88,89). 86 debukki tulostaa merkkijonon, jota käytetään konversioissa:
#ifdef DEBUG86
// ./newressu: digits string used: "0123456789", length: 10
fprintf(stderr,"%s:", procname);
fprintf(stderr," digits string used: \"%s\"", digits);
fprintf(stderr,", length: %d", d);
fprintf(stderr,"\n");
#endif
87 debukki tulostaa merkin numeron ja merkin. Merkit voivat olla ascii merkkejä tai utf8 merkkejä, joissa sisäinen muoto koostuu kahdesta tai useammasta merkistä:
#ifdef DEBUG87
// ./newressu: digits has characters 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9
fprintf(stderr,"%s:", procname);
fprintf(stderr," digits has characters");
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
fprintf(stderr," %d:%s",c,character);
}
fprintf(stderr,"\n");
#endif
Tämä 88 debukki näyttää digits merkkijonon merkkien järjestysnumeron ja tulostusmerkkijonon heksana.
#ifdef DEBUG88
//./newressu: characters have codes 0:30 1:31 2:32 3:33 4:34 5:35 6:36 7:37 8:38 9:39
fprintf(stderr,"%s:", procname);
fprintf(stderr," characters have codes");
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
p=character;
fprintf(stderr," %d:%02x",c,*p); // first character
p++;
while(*p!='\0') {
if(*p>0xbf || // first utf8 char
*p<0x80 || // ascii char
*p=='\0') // end of file
break;
fprintf(stderr,"%02x",*p); // rest of the characters
p++;
}
}
fprintf(stderr,"\n");
#endif
} // if(digits!=NULL)
89 debukki tulostaa rivin vain jos merkkijonossa on utf8 merkkejä. Tämä näyttää merkin utf8 koodin kun taas edellinen näytti tulostusmerkkijonon.
#ifdef DEBUG89
// ./newressu: characters have utf8-codes 0:030 1:031 2:032 3:033 4:034 5:035 6:036 7:037 8:038 9:039
// 0c0 ÀÁÂÃÄÅÆÇ ÈÉÊËÌÍÎÏ
// 0d0 ÐÑÒÓÔÕÖ× ØÙÚÛÜÝÞß
// 0e0 àáâãäåæç èéêëìíîï
// 0f0 ðñòóôõö÷ øùúûüýþÿ
int utffound=0;
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
if(strlen(character)>1) {
utffound=1;
break;
}
}
if(utffound) {
fprintf(stderr,"%s:", procname);
fprintf(stderr," characters have utf8-codes");
for(c=0;c<d;c++) {
unsigned long code;
utf8getchar(sizeof(character),character,c,digits);
p=character;
// first byte
if(*p>=0xfc) code=*p&0x1; // 1111110 x 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 7fffffff
else if(*p>=0xf8) code=*p&0x3; // 111110 xx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
if(*p>=0xf0) code=*p&0x7; // 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
if(*p>=0xe0) code=*p&0xf; // 1110 xxxx 10 xxxxxx 10 xxxxxx
if(*p>=0xc0) code=*p&0x1f; // 110 xxxxx 10 xxxxxx
code=*p&0x7f; // ascii
while(*p!='\0') {
if(*p>0xbf || // first utf8 byte
*p<0x80 || // ascii char
*p=='\0') // end of string
break;
code=code<<6|*p&0x3f;
p++;
}
fprintf(stderr," %d:0%lx",c,code);
}
fprintf(stderr,"\n");
} // if(utffound) {
#endif
Ohjelmanpätkä hakee rivinumeron pituuden merkkeinä: Aluksi lines muuttujasta vähennetään yksi, muutetaan se merkkijonoksi out_word toiminnolla. Otetaan edellisen vastauksen pituus merkkeinä. Jos vastaus on lyhyempi kuin 5 merkkiä vastaus on viisi merkkiä.
// get linenumber length in digits
out_word(sizeof(wordbuf),wordbuf,"0123456789",lines-1);
plinesdigits=strlen(wordbuf);
if(plinesdigits<5)
plinesdigits=5;
limit muuttujaan määritetään arvo –lim kytkimellä. Menetelmä on sama kun edellisessä kappaleessa ilman viiden minimiä ja edellisessä tulostetaan aina desimaalilukuja.
// get data length in digits
if(limit!=0) {
unsigned char wordbuf[128];
out_word(sizeof(wordbuf),wordbuf,digits,limit-1);
size=utf8len(wordbuf);
}
Tämä rutiini tulostaa sanan, jos sille on määritelty yläraja (limitti). poistetaan sanojen joukosta nollat, tulostetaan ja täytetään etunollat.
if(limit!=0) {
word=0;
if(zero) {
word=ressu_gen_limit(limit); // include zeroes
} else if(limit>=1) {
while((word=ressu_gen_limit(limit))==0); // skip zeroes
}
out_word(sizeof(wordbuf3),wordbuf3,digits,word);
// fill leading zeroes
wordbuf[0]='\0';
utf8getchar(sizeof(character),character,0,digits);
for(c=size-utf8len(wordbuf3);c>0;c--) {
strcat(wordbuf,character);
}
// rest of the number
strcat(wordbuf,wordbuf3);
}
Tulostus jos ei ole määritelty ylärajaa. Tällöin käytetään merkkien rajoitteena digits merkkijonoa.
if(digits!=NULL) {
int digitslen;
wordbuf[0]='\0';
digitslen=utf8len(digits);
// fill whole word digit by digit
for(c=0;c<size;c++) {
if(digits[0]=='0' && !zero)
d=ressu_gen_limit(digitslen-1)+1;
else
d=ressu_gen_limit(digitslen);
utf8getchar(sizeof(wordbuf3),wordbuf3,d,digits);
strcat(wordbuf,wordbuf3);
}
}
Tässä rutiinissa katsotaan tarvitaanko rivinvaihto ennen seuraavaa sanaa, eli jos tulostetun rivin pituus ja utf8 merkkijonon pituus ylittää rivin pituuden, rivin pituus ja ja sanan pituus ylittää rivin pituuden tai tulostettujen sanojen lukumäärä ylittää sanojen lukumäärän tulostetaan rivinvaihto. Lisäksi tässä on käsittely rand tyyppisille riveille (sspace==3). Rand tyylisessä taulussa tulostetaan viiden rivin jälkeen ylimääräinen välilyönti, eli tulostetaan tyhjä rivi. Tyhjä rivi tulostetaan vain jos seuraavalla rivillä on tulostettavaa (plines<lines).
// line full?
if(((chars > 0 && pchars+utf8len(wordbuf)>=chars) ||
(chars > 0 && pchars+size>=chars) ||
(words > 0 && pwords>=words))
&&pwords > 0) { // atleast one word printed
pchars=0;
pwords=0;
if(!quiet)
fprintf(stdout,"\n");
plines++;
if(sspace==3 && plines<lines && plines%5==0)
fprintf(stdout,"\n");
fflush(stdout);
}
Jos tulostetun rivin numero sivuaa haluttua rivien määrää poistutaan for(;;) luupista.
// all needed lines printed?
if(plines >= lines)
break;
Jos olemme rivin alussa (pchars == 0) ja rivinumeron tulostusta ei ole estetty tulostetaan se.
// in beginning of line, print line number
if(pchars == 0) {
sprintf(wordbuf2,"%0*d",plinesdigits,plines);
if(!quiet && slines) {
fprintf(stdout,"%s",wordbuf2);
pchars +=strlen(wordbuf2);
fprintf(stdout," ");
pchars++;
}
}
Tulostetaanko välilyönti sanojen väliin. Tässä myös rand käsittelyä (sspace==3). Rand tyylisessä taulukossa sanojen väliin tulostetaan välilyönti ja kahden sanan välein tulostetaan toinen välilyönti. Rivinumeron ja ensimmäisen sanan väliin tulee kolme välilyöntiä. Eli sanojen välilyönnit ovat 3 1 2 1 2 1 2 1 2 1.
// want to print spaces between "words"?
if(sspace && pchars>0) {
if(!quiet) {
if(sspace==3 && pwords%2==0)
fprintf(stdout," ");
fprintf(stdout," ");
}
pchars++;
}
Ja viimeinen rutiini, varsinaisen sanan tulostus:
// print word
if(size!=0) {
if(!quiet)
fprintf(stdout,"%*s",size,wordbuf);
pchars += size;
pwords++;
} else {
if(!quiet)
fprintf(stdout,"%s",wordbuf);
pchars += strlen(wordbuf);
pwords++;
}
Seuraavassa leluohjelma kokonaisuudessaan:
#define aDEBUG75 2
#define aDEBUG76 2
#define aDEBUG86 2
#define aDEBUG87 2
#define aDEBUG88 2
#define aDEBUG89 2
static char *programname = "newressu version 2.0 ©";
static unsigned char *copyright = "Copyright (c) 2013-2021 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";
void main(int argc, char *argv[])
{
int c, d, bytes=1024, size=5, sspace=0,
chars=72, pchars=0, words=0, pwords=0,
lines=10, plines=0, plinesdigits=5, slines=1,
stats=0, quiet=0, zero=1;
unsigned char *p, buffer[32], *digits="0123456789",
digitstemp[256], character[32];
unsigned long limit;
procname=argv[0];
limit=0;
// look thru command line parameters
for(c=1;c<argc;c++) {
if(!strncmp("-",argv[c],1)) {
if(!strcmp("--lineno",argv[c])) {
slines=!slines;
} 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("--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++;
}
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++;
}
} 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++;
}
} 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;
slines=1;
words=10;
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 {
fprintf(stderr,"%s: invalid option %s\n",procname,argv[c]);
help = 1;
}
} else {
help = 1;
} // if(!strncmp
} // for(c=0
if(digits!=NULL) {
d = utf8len(digits);
#ifdef DEBUG86
// ./newressu: digits string used: "0123456789", length: 10
fprintf(stderr,"%s:", procname);
fprintf(stderr," digits string used: \"%s\"", digits);
fprintf(stderr,", length: %d", d);
fprintf(stderr,"\n");
#endif
#ifdef DEBUG87
// ./newressu: digits has characters 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9
fprintf(stderr,"%s:", procname);
fprintf(stderr," digits has characters");
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
fprintf(stderr," %d:%s",c,character);
}
fprintf(stderr,"\n");
#endif
#ifdef DEBUG88
//./newressu: characters have codes 0:30 1:31 2:32 3:33 4:34 5:35 6:36 7:37 8:38 9:39
fprintf(stderr,"%s:", procname);
fprintf(stderr," characters have codes");
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
p=character;
fprintf(stderr," %d:%02x",c,*p); // first character
p++;
while(*p!='\0') {
if(*p>0xbf || // first utf8 char
*p<0x80 || // ascii char
*p=='\0') // end of file
break;
fprintf(stderr,"%02x",*p); // rest of the characters
p++;
}
}
fprintf(stderr,"\n");
#endif
#ifdef DEBUG89
// ./newressu: characters have utf8-codes 0:030 1:031 2:032 3:033 4:034 5:035 6:036 7:037 8:038 9:039
// 0c0 ÀÁÂÃÄÅÆÇ ÈÉÊËÌÍÎÏ
// 0d0 ÐÑÒÓÔÕÖ× ØÙÚÛÜÝÞß
// 0e0 àáâãäåæç èéêëìíîï
// 0f0 ðñòóôõö÷ øùúûüýþÿ
int utffound=0;
for(c=0;c<d;c++) {
utf8getchar(sizeof(character),character,c,digits);
if(strlen(character)>1) {
utffound=1;
break;
}
}
if(utffound) {
fprintf(stderr,"%s:", procname);
fprintf(stderr," characters have utf8-codes");
for(c=0;c<d;c++) {
unsigned long code;
utf8getchar(sizeof(character),character,c,digits);
p=character;
// first byte
if(*p>=0xfc) code=*p&0x1; // 1111110 x 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 7fffffff
else if(*p>=0xf8) code=*p&0x3; // 111110 xx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
else if(*p>=0xf0) code=*p&0x7; // 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
else if(*p>=0xe0) code=*p&0xf; // 1110 xxxx 10 xxxxxx 10 xxxxxx
else if(*p>=0xc0) code=*p&0x1f; // 110 xxxxx 10 xxxxxx
else code=*p&0x7f; // ascii
p++;
while(*p!='\0') {
if(*p>0xbf || // first utf8 byte
*p<0x80 || // ascii char
*p=='\0') // end of string
break;
code=code<<6|*p&0x3f;
p++;
}
fprintf(stderr," %d:0%lx",c,code);
}
fprintf(stderr,"\n");
} // if(utffound) {
#endif
} // if(digits!=NULL)
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 --isalnum", "print alphanumeric characters",
"newressu --isalpha", "print alphabetic characters",
"newressu --isdigit", "print digits",
"newressu --isgraph", "print printables excluding space",
"newressu --islower", "print lowercase characters",
"newressu --isprint", "print printable characters including space",
"newressu --ispunct", "print punctuation characters",
"newressu --isupper", "print uppercase characters",
"newressu --isxdigit", "print hexadecimal digits",
"newressu -1", "print material for passwords",
"newressu -iaeiouyåäö", "print random vowels",
"newressu -iаэыуояеёюи", "print russian vowels",
"newressu -s -i ","flags",
"newressu -i \"/\\\"", "print nice labyrinth",
"newressu --stats", "print statistics for ressu run",
"newressu --rand -l20000", "print rand style random number table",
};
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");
lines=1; // print one line below help as a sample
}
unsigned long word;
unsigned char wordbuf[1024];
unsigned char wordbuf2[1024];
unsigned char wordbuf3[1024];
// get linenumber length in digits
out_word(sizeof(wordbuf),wordbuf,"0123456789",lines-1);
plinesdigits=strlen(wordbuf);
if(plinesdigits<5)
plinesdigits=5;
// get data length in digits
if(limit!=0) {
unsigned char wordbuf[128];
out_word(sizeof(wordbuf),wordbuf,digits,limit-1);
size=utf8len(wordbuf);
}
// init statistics
for(;;) {
if(limit!=0) {
word=0;
if(zero) {
word=ressu_gen_limit(limit); // include zeroes
} else if(limit>=1) {
while((word=ressu_gen_limit(limit))==0); // skip zeroes
}
out_word(sizeof(wordbuf3),wordbuf3,digits,word);
// fill leading zeroes
wordbuf[0]='\0';
utf8getchar(sizeof(character),character,0,digits);
for(c=size-utf8len(wordbuf3);c>0;c--) {
strcat(wordbuf,character);
}
// rest of the number
strcat(wordbuf,wordbuf3);
} else if(digits!=NULL) {
int digitslen;
wordbuf[0]='\0';
digitslen=utf8len(digits);
// fill whole word digit by digit
for(c=0;c<size;c++) {
if(digits[0]=='0' && !zero)
d=ressu_gen_limit(digitslen-1)+1;
else
d=ressu_gen_limit(digitslen);
utf8getchar(sizeof(wordbuf3),wordbuf3,d,digits);
strcat(wordbuf,wordbuf3);
}
}
// line full?
if(((chars > 0 && pchars+utf8len(wordbuf)>=chars) ||
(chars > 0 && pchars+size>=chars) ||
(words > 0 && pwords>=words))
&&pwords > 0) { // atleast one word printed
pchars=0;
pwords=0;
if(!quiet)
fprintf(stdout,"\n");
plines++;
if(sspace==3 && plines<lines && plines%5==0)
fprintf(stdout,"\n");
fflush(stdout);
}
// all needed lines printed?
if(plines >= lines)
break;
// all needed lines printed?
if(plines >= lines)
break;
// in beginning of line, print line number
if(pchars == 0) {
sprintf(wordbuf2,"%0*d",plinesdigits,plines);
if(!quiet && slines) {
fprintf(stdout,"%s",wordbuf2);
pchars +=strlen(wordbuf2);
fprintf(stdout," ");
pchars++;
}
}
// want to print spaces between "words"?
if(sspace && pchars>0) {
if(!quiet) {
if(sspace==3 && pwords%2==0)
fprintf(stdout," ");
fprintf(stdout," ");
}
pchars++;
}
// print word
if(size!=0) {
if(!quiet)
fprintf(stdout,"%*s",size,wordbuf);
pchars += size;
pwords++;
} else {
if(!quiet)
fprintf(stdout,"%s",wordbuf);
pchars += strlen(wordbuf);
pwords++;
}
} // for(;;)
}