After fixing the undefined reference to 'pow'
by adding -lm
at the end of the cc
line, you have another error:
charset_length
is 62. max
is 8.
So, when you do:
int num_combinations = pow(charset_length, max);
You are doing pow(62,8)
which, when truncated to an int
, you get a negative number. The result of the pow
is on the order of 2.1e14, so far too large for an int
.
So, the subsequent for
loop will execute zero times.
You may want to use long long
instead, but 2e14 iterations seems like a lot.
Doing:
int num_combinations = pow(charset_length, max - min);
seems more reasonable (and fits inside an int
). But, in that case, your program just outputs blank lines.
UPDATE
When I've done a similar type of iteration, I've used a "helper" vector that is the current number width in length. I treat this an an N digit wide number in base(charset_length).
It takes values for each digit in the range of 0 to charset_length - 1.
I think it's easier to have the outer loop be output word length (e.g. 4-8) and then generate all words of that length from the charset
So, I had to do some considerable refactoring, splitting up the generation code into multiple functions.
The actual runtime is so large that I added progress output to stdout
...
Here is the refactored code. It is annotated:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
// incbignum -- increment the base N number
// RETURNS: carryout/overflow
int
incbignum(unsigned char *bignum,int iwid,int base)
{
int carry = 1;
for (int idx = 0; idx < iwid; ++idx) {
int val = bignum[idx];
val += carry;
carry = (val >= base);
if (carry)
val %= base;
bignum[idx] = val;
if (! carry)
break;
}
return carry;
}
// tscgetf -- get time in fractional seconds
double
tscgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_MONOTONIC,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
return sec;
}
// genlength -- generate all words of a given length from all chars in charset
void
genlength(FILE *file,const char *charset,int iwid)
{
int charset_length = strlen(charset);
unsigned long long wordcount = 0;
char buffer[iwid + 1];
// create a number in base(charset_length) with iwid number of digits
unsigned char bignum[iwid];
memset(bignum,0,iwid);
// show some progress
printf("genlength: iwid=%d\n",iwid);
double tscold = tscgetf();
while (1) {
// get the current output word
for (int idx = 0; idx < iwid; ++idx)
buffer[idx] = charset[bignum[idx]];
buffer[iwid] = 0;
// send it to the file
fputs(buffer,file);
fputc('\n',file);
// advance the base(charset_length) number
if (incbignum(bignum,iwid,charset_length))
break;
// show progress
if ((++wordcount % 3761) == 0) {
double tscnow = tscgetf();
if ((tscnow - tscold) >= 1.0) {
printf("\rwordcount=%llu %s",wordcount,buffer);
fflush(stdout);
tscold = tscnow;
}
}
}
printf("\nfinal=%llu\n",wordcount);
}
void
Dictionary_Generator(const char *charset, int min, int max)
{
char *password = malloc((max + 1) * sizeof(char));
if (password == NULL) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(1);
}
FILE *file = fopen("dictionary.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: could not create file\n");
exit(1);
}
int charset_length = strlen(charset);
#if 0
int num_combinations = pow(charset_length, max);
#else
int num_combinations = pow(charset_length, max - min);
#endif
printf("num_combinations=%d\n",num_combinations);
#if 0
for (int i = 0; i < num_combinations; i++) {
int index = i;
int length = min;
while (index >= charset_length) {
password[length - 1] = charset[index % charset_length];
index /= charset_length;
length++;
}
password[length - 1] = charset[index];
password[length] = '\0';
if (length >= min) {
int result = fprintf(file, "%s\n", password);
if (result < 0) {
perror("Error writing to file");
exit(1);
}
}
}
#else
for (int iwid = min; iwid <= max; ++iwid)
genlength(file,charset,iwid);
#endif
fclose(file);
free(password);
}
int
main()
{
// NOTE -- the full charset takes a _long_ time ... :-)
#if TEST
const char *charset = "abcdefghijklmnopqrstuvwxyz";
#else
const char *charset = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789";
#endif
int min = 4;
int max = 8;
Dictionary_Generator(charset, min, max);
return 0;
}