diff -uN pwgen-2.03/ChangeLog pwgen-2.04/ChangeLog
--- pwgen-2.03/ChangeLog	2003-01-16 03:29:46.000000000 +0100
+++ pwgen-2.04/ChangeLog	2005-01-08 21:50:50.000000000 +0100
@@ -1,9 +1,61 @@
+2005-01-08  Olivier Guerrier  <olivier@guerrier.com>
+
+	* Proposal for a 2.04 release
+
+	* General cleanup, remove some unused variable and structure 
+		declarations, make code compile without warning
+	
+	* pw_phonemes.c: Modify pw_element and pw_phonemes to generate more 
+		readable, but (I hope) more secure passwords.
+		Based on comments from Debian bug #276976, but does not 
+		adresses it. (Human readeable passwords cannot be as secure 
+		as pure random password, it's obvious)
+	
+	* pw_rand.c: Take care of pw_flags to generate passwords without
+		capitals and/or without digits. Adresse Debian bug #276307
+
+	* pw_rand.c: Fix what seems to be a bug, to allow more than one
+		digit and more than one capital in passwords.
+		Fix Debian bug #182595
+
+	* sha1.c: Gpl'd lib to compute sha1 hash, contributed by 
+		Christophe Devine
+
+	* sha1num.c: Add an alternative to true random numbers, by using
+		sha1 hash of given file and given seed. 
+		
+	* pwgen.c: Add -B, --ambiguous option, to prevent use of ambiguous 
+		characters in generated passwords. (ie: 2/Z, 0/O, etc...)
+		Adresses (?) Debian bug #51307
+		This option is effective in pw_rand and pw_phonemes
+
+	* pwgen.c: Make -A an alias to --no-capitalize, and make this option 
+		effective in pw_rand. 
+
+	* pwgen.c: Make -0 an alias to --no-numerals and make this option 
+		effective in pw_rand.
+
+	* pwgen.c: Add -H, --sha1 option, to allow computing reproducibles
+		passwords, given a know file, and a know seed.
+		(ie: pwgen -H ~/my_favourite.mp3#olivier@guerrier.com gives me
+		a list of possibles passwords for my pop3 account, and I can
+		ask this list again and again)
+		This option is effective in pw_rand and pw_phonemes
+
+	* pwgen.c: Add -y, --symbols option, which add one one more specials
+		characters in generated passwords
+		This option is effective in pw_rand and pw_phonemes
+	
+	* pwgen.1: Updated with new options and features  
+
+	* Makefile.in: Add sha1.c sha1.h sha1num.c where needed
+	
 2003-01-15  Theodore Ts'o  <tytso@mit.edu>
 
 	* Release of pwgen 2.03
 
 	* randnum.c: Fix #ifdef to use HAVE_DRAND48 instead of just
-		RAND48.  This caused random() to be callled without first
+		RAND48.  This caused random() to be called without first
 		initializing the right random number generator.  This
 		apparently caused pwgen to core dump under AIX, which
 		seems surprising.
diff -uN pwgen-2.03/Makefile.in pwgen-2.04/Makefile.in
--- pwgen-2.03/Makefile.in	2003-01-16 03:29:27.000000000 +0100
+++ pwgen-2.04/Makefile.in	2005-01-08 14:31:22.000000000 +0100
@@ -31,9 +31,9 @@
 .c.o:
 	$(CC) -c $(ALL_CFLAGS) $< -o $@
 
-OBJS= pwgen.o pw_phonemes.o pw_rand.o randnum.o
+OBJS= pwgen.o pw_phonemes.o pw_rand.o randnum.o sha1.o sha1num.o
 
-SRCS= pwgen.c pw_phonemes.c pw_rand.c randnum.c
+SRCS= pwgen.c pw_phonemes.c pw_rand.c randnum.c sha1.c sha1num.c
 
 
 pwgen: $(OBJS)
@@ -127,3 +127,5 @@
 pw_phonemes.o: pw_phonemes.c pwgen.h
 pw_rand.o: pw_rand.c pwgen.h
 randnum.o: randnum.c pwgen.h
+sha1.o: sha1.c sha1.h 
+sha1num.o: sha1num.c sha1.h pwgen.h
diff -uN pwgen-2.03/pw_phonemes.c pwgen-2.04/pw_phonemes.c
--- pwgen-2.03/pw_phonemes.c	2003-01-16 01:18:46.000000000 +0100
+++ pwgen-2.04/pw_phonemes.c	2005-01-08 19:49:06.000000000 +0100
@@ -13,54 +13,147 @@
 
 struct pw_element elements[] = {
 	{ "a",	VOWEL },
-	{ "ae", VOWEL | DIPTHONG },
-	{ "ah",	VOWEL | DIPTHONG },
-	{ "ai", VOWEL | DIPTHONG },
+	{ "ae", VOWEL },
+	{ "ai", VOWEL },
+	{ "ao", VOWEL },
+	{ "au", VOWEL },
+	{ "ay", VOWEL },
 	{ "b",  CONSONANT },
+	{ "bh", CONSONANT },
+	{ "bl", CONSONANT },
+	{ "br", CONSONANT },
 	{ "c",	CONSONANT },
-	{ "ch", CONSONANT | DIPTHONG },
+	{ "cc",	CONSONANT },
+	{ "ch", CONSONANT },
+	{ "ck", CONSONANT | NOT_FIRST},
+	{ "cl", CONSONANT },
+	{ "cr",	CONSONANT },
 	{ "d",	CONSONANT },
+	{ "dh",	CONSONANT },
+	{ "dl",	CONSONANT },
+	{ "dm",	CONSONANT | NOT_FIRST},
+	{ "dr",	CONSONANT },
 	{ "e",	VOWEL },
-	{ "ee", VOWEL | DIPTHONG },
-	{ "ei",	VOWEL | DIPTHONG },
+	{ "ea",	VOWEL },
+	{ "ee", VOWEL },
+	{ "ei",	VOWEL },
+	{ "eo",	VOWEL },
+	{ "eu",	VOWEL },
+	{ "ey",	VOWEL },
 	{ "f",	CONSONANT },
+	{ "fh",	CONSONANT },
+	{ "fl",	CONSONANT },
+	{ "fr",	CONSONANT },
 	{ "g",	CONSONANT },
-	{ "gh", CONSONANT | DIPTHONG | NOT_FIRST },
+	{ "gh", CONSONANT },
+	{ "gl", CONSONANT },
+	{ "gr",	CONSONANT },
+	{ "gs",	CONSONANT },
 	{ "h",	CONSONANT },
 	{ "i",	VOWEL },
-	{ "ie", VOWEL | DIPTHONG },
+	{ "ia",	VOWEL },
+	{ "ie", VOWEL },
+	{ "io",	VOWEL },
+	{ "iu",	VOWEL },
 	{ "j",	CONSONANT },
+	{ "jr",	CONSONANT },
 	{ "k",	CONSONANT },
+	{ "kh",	CONSONANT },
+	{ "kl",	CONSONANT },
+	{ "kr",	CONSONANT },
 	{ "l",	CONSONANT },
+	{ "ld",	CONSONANT },
+	{ "lh",	CONSONANT },
+	{ "ll",	CONSONANT | NOT_FIRST},
+	{ "lt",	CONSONANT | NOT_FIRST},
 	{ "m",	CONSONANT },
+	{ "mp",	CONSONANT | NOT_FIRST},
+	{ "mpl",CONSONANT | NOT_FIRST},
+	{ "mpr",CONSONANT | NOT_FIRST},
+	{ "mm",	CONSONANT | NOT_FIRST},
+	{ "mn",	CONSONANT | NOT_FIRST},
+	{ "mr",	CONSONANT | NOT_FIRST},
+	{ "mt",	CONSONANT | NOT_FIRST},
 	{ "n",	CONSONANT },
-	{ "ng",	CONSONANT | DIPTHONG | NOT_FIRST },
+	{ "nc",	CONSONANT },
+	{ "nct",CONSONANT | NOT_FIRST },
+	{ "ng",	CONSONANT | NOT_FIRST },
+	{ "nt",	CONSONANT },
 	{ "o",	VOWEL },
-	{ "oh",	VOWEL | DIPTHONG },
-	{ "oo",	VOWEL | DIPTHONG},
+	{ "oa",	VOWEL },
+	{ "oe",	VOWEL },
+	{ "oi",	VOWEL },
+	{ "oo",	VOWEL },
+	{ "ou",	VOWEL },
+	{ "oy",	VOWEL },
 	{ "p",	CONSONANT },
-	{ "ph",	CONSONANT | DIPTHONG },
-	{ "qu",	CONSONANT | DIPTHONG},
+	{ "ph",	CONSONANT },
+	{ "pl",	CONSONANT },
+	{ "pr",	CONSONANT },
+	{ "ps",	CONSONANT },
+	{ "pt",	CONSONANT },
+	{ "qu",	CONSONANT },
 	{ "r",	CONSONANT },
+	{ "rc",	CONSONANT | NOT_FIRST},
+	{ "rd",	CONSONANT | NOT_FIRST},
+	{ "rds",CONSONANT | NOT_FIRST},
+	{ "rm",	CONSONANT | NOT_FIRST},
+	{ "rn",	CONSONANT | NOT_FIRST},
+	{ "rr",	CONSONANT | NOT_FIRST},
+	{ "rs",	CONSONANT | NOT_FIRST},
+	{ "rt",	CONSONANT | NOT_FIRST},
+	{ "rts",CONSONANT | NOT_FIRST},
 	{ "s",	CONSONANT },
-	{ "sh",	CONSONANT | DIPTHONG},
+	{ "sh",	CONSONANT },
+	{ "sk",	CONSONANT },
+	{ "sr",	CONSONANT | NOT_FIRST},
+	{ "ss",	CONSONANT | NOT_FIRST},
+	{ "st",	CONSONANT },
+	{ "str",CONSONANT },
+	{ "sw",	CONSONANT },
 	{ "t",	CONSONANT },
-	{ "th",	CONSONANT | DIPTHONG},
+	{ "tt",	CONSONANT | NOT_FIRST},
+	{ "th",	CONSONANT },
+	{ "tl",	CONSONANT },
+	{ "tr",	CONSONANT },
+	{ "tw",	CONSONANT },
 	{ "u",	VOWEL },
+	{ "ua",	VOWEL },
+	{ "ue",	VOWEL },
+	{ "ui",	VOWEL },
+	{ "uo",	VOWEL },
+	{ "uy",	VOWEL },
 	{ "v",	CONSONANT },
+	{ "vl",	CONSONANT },
+	{ "vr",	CONSONANT },
 	{ "w",	CONSONANT },
+	{ "wh",	CONSONANT },
+	{ "wr",	CONSONANT },
 	{ "x",	CONSONANT },
-	{ "y",	CONSONANT },
-	{ "z",	CONSONANT }
+	{ "xp",	CONSONANT },
+	{ "y",	VOWEL },
+	{ "ya",	VOWEL },
+	{ "ye",	VOWEL },
+	{ "yi",	VOWEL },
+	{ "yo",	VOWEL },
+	{ "yu",	VOWEL },
+	{ "z",	CONSONANT },
+	{ "zl",	CONSONANT },
+	{ "zw",	CONSONANT },
+	{ "zr",	CONSONANT }
 };
 
 #define NUM_ELEMENTS (sizeof(elements) / sizeof (struct pw_element))
 
-void pw_phonemes(char *buf, int size, int pw_flags)
+void pw_phonemes(buf, size, pw_flags)
+	char *buf;
+	int size;
+	int pw_flags;
 {
 	int		c, i, len, flags, feature_flags;
 	int		prev, should_be, first;
 	const char	*str;
+	char		ch, *chptr;
 
 try_again:
 	feature_flags = pw_flags;
@@ -69,59 +162,88 @@
 	should_be = 0;
 	first = 1;
 
-	should_be = pw_random_number(2) ? VOWEL : CONSONANT;
+	should_be = pw_number(2) ? VOWEL : CONSONANT;
 	
 	while (c < size) {
-		i = pw_random_number(NUM_ELEMENTS);
+		i = pw_number(NUM_ELEMENTS);
 		str = elements[i].str;
 		len = strlen(str);
 		flags = elements[i].flags;
 		/* Filter on the basic type of the next element */
-		if ((flags & should_be) == 0)
+		if ((flags & should_be) == 0) {
 			continue;
+		}
 		/* Handle the NOT_FIRST flag */
-		if (first && (flags & NOT_FIRST))
-			continue;
-		/* Don't allow VOWEL followed a Vowel/Dipthong pair */
-		if ((prev & VOWEL) && (flags & VOWEL) &&
-		    (flags & DIPTHONG))
+		if (first && (flags & NOT_FIRST)) {
 			continue;
+		}
 		/* Don't allow us to overflow the buffer */
-		if (len > size-c)
+		if (len > size-c) {
 			continue;
+		}
 		/*
 		 * OK, we found an element which matches our criteria,
 		 * let's do it!
 		 */
 		strcpy(buf+c, str);
 
-		/* Handle PW_ONE_CASE */
-		if (feature_flags & PW_ONE_CASE) {
-			if ((first || flags & CONSONANT) &&
-			    (pw_random_number(10) < 3)) {
+		/* Handle PW_UPPERS */
+		if (pw_flags & PW_UPPERS) {
+			if (pw_number(10) < 2) {
 				buf[c] = toupper(buf[c]);
-				feature_flags &= ~PW_ONE_CASE;
+				feature_flags &= ~PW_UPPERS;
 			}
 		}
 		
 		c += len;
 		
+		/* Handle the AMBIGUOUS flag */
+		if (pw_flags & PW_AMBIGUOUS) {
+			chptr = strpbrk(buf, pw_ambiguous);
+			if (chptr) {
+				*chptr = 0;
+				c = strlen(buf);
+			}
+		}
+		
 		/* Time to stop? */
 		if (c >= size)
 			break;
 		
-		/*
-		 * Handle PW_ONE_NUMBER
-		 */
-		if (feature_flags & PW_ONE_NUMBER) {
-			if (!first && (pw_random_number(10) < 3)) {
-				buf[c++] = pw_random_number(10)+'0';
+		/* Handle PW_DIGITS */
+		if (pw_flags & PW_DIGITS) {
+			if (!first && (pw_number(10) < 3)) {
+				do {
+					ch = pw_number(10)+'0';
+				} while ((pw_flags & PW_AMBIGUOUS) 
+					&& strchr(pw_ambiguous, ch));
+				buf[c++] = ch;
+				buf[c] = 0;
+				feature_flags &= ~PW_DIGITS;
+				
+				first = 1;
+				prev = 0;
+				should_be = pw_number(2) ?
+					VOWEL : CONSONANT;
+				continue;
+			}
+		}
+		
+		/* Handle PW_SYMBOLS */
+		if (pw_flags & PW_SYMBOLS) {
+			if (!first && (pw_number(10) < 2)) {
+				do {
+					ch = pw_symbols[
+						pw_number(strlen(pw_symbols))];
+				} while ((pw_flags & PW_AMBIGUOUS) 
+					&& strchr(pw_ambiguous, ch));
+				buf[c++] = ch;
 				buf[c] = 0;
-				feature_flags &= ~PW_ONE_NUMBER;
+				feature_flags &= ~PW_SYMBOLS;
 				
 				first = 1;
 				prev = 0;
-				should_be = pw_random_number(2) ?
+				should_be = pw_number(2) ?
 					VOWEL : CONSONANT;
 				continue;
 			}
@@ -132,17 +254,13 @@
 		 */
 		if (should_be == CONSONANT) {
 			should_be = VOWEL;
-		} else { /* should_be == VOWEL */
-			if ((prev & VOWEL) ||
-			    (flags & DIPTHONG) ||
-			    (pw_random_number(10) > 3))
-				should_be = CONSONANT;
-			else
-				should_be = VOWEL;
+		} 
+		else {
+			should_be = CONSONANT;
 		}
 		prev = flags;
 		first = 0;
 	}
-	if (feature_flags & (PW_ONE_CASE | PW_ONE_NUMBER))
+	if (feature_flags & (PW_UPPERS | PW_DIGITS | PW_SYMBOLS))
 		goto try_again;
 }
diff -uN pwgen-2.03/pw_rand.c pwgen-2.04/pw_rand.c
--- pwgen-2.03/pw_rand.c	2003-01-16 02:24:22.000000000 +0100
+++ pwgen-2.04/pw_rand.c	2005-01-08 05:27:18.000000000 +0100
@@ -9,20 +9,64 @@
  */
 
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include "pwgen.h"
 
-#define PW_CHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-
-void pw_rand(char *buf, int size, int pw_flags)
+void pw_rand(buf, size, pw_flags)
+	char *buf;
+	int size;
+	int pw_flags;
 {
-	char	ch, *chars = PW_CHARS;
+	char	ch;
+	char	*chars, *wchars;
 	int	i = 0, len;
 
-	len = strlen(chars);
+	len = 0;
+	if(pw_flags & PW_DIGITS) {
+		len += strlen(pw_digits);
+	}
+	if(pw_flags & PW_UPPERS) {
+		len += strlen(pw_uppers);
+	}
+	if(pw_flags & PW_LOWERS) {
+		len += strlen(pw_lowers);
+	}
+	if(pw_flags & PW_SYMBOLS) {
+		len += strlen(pw_symbols);
+	}
+        chars = malloc(len+1);
+        if (!chars) {
+		fprintf(stderr, "Couldn't malloc pw_rand buffer.\n");
+		exit(1);
+	}
+	wchars = chars;
+	if(pw_flags & PW_DIGITS) {
+		strcpy(wchars, pw_digits);
+		wchars += strlen(pw_digits);
+	}
+	if(pw_flags & PW_UPPERS) {
+		strcpy(wchars, pw_uppers);
+		wchars += strlen(pw_uppers);
+	}
+	if(pw_flags & PW_LOWERS) {
+		strcpy(wchars, pw_lowers);
+		wchars += strlen(pw_lowers);
+	}
+	if(pw_flags & PW_SYMBOLS) {
+		strcpy(wchars, pw_symbols);
+	}
+				
 	while (i < size) {
-		ch = chars[pw_random_number(len)];
+		ch = chars[pw_number(len)];
+		if (pw_flags & PW_AMBIGUOUS) {
+			if (strchr(pw_ambiguous,ch)) {
+				continue;
+			}
+		}
 		buf[i++] = ch;
 	}
 	buf[size] = 0;
+	free(chars);
 	return;
 }	
diff -uN pwgen-2.03/pw_test.sh pwgen-2.04/pw_test.sh
--- pwgen-2.03/pw_test.sh	1970-01-01 01:00:00.000000000 +0100
+++ pwgen-2.04/pw_test.sh	2005-01-08 14:17:24.000000000 +0100
@@ -0,0 +1,2 @@
+
+./pwgen 8 100000 | perl -ne '$_=lc $_; $len=length $_; for ($x=0; $x < $len-2; $x++) { $f{substr($_, $x, 2)}++ }; END { print map { $_="$f{$_}\t$_\n" } keys %f }' |sort -rn | head -10
diff -uN pwgen-2.03/pwgen.1 pwgen-2.04/pwgen.1
--- pwgen-2.03/pwgen.1	2003-01-16 02:23:10.000000000 +0100
+++ pwgen-2.04/pwgen.1	2005-01-08 21:58:05.000000000 +0100
@@ -1,4 +1,4 @@
-.TH PWGEN 1 "July 2002" "pwgen version 2.02"
+.TH PWGEN 1 "January 2005" "pwgen version 2.04"
 .SH NAME
 pwgen \- generate pronounceable passwords
 .SH SYNOPSIS
@@ -45,25 +45,27 @@
 Include at least one capital letter in the password.  This is the default 
 if the standard output is a tty device.
 .TP
-.B \-C
-Print the generated passwords in columns.  This is the default if the 
-standard output is a tty device.
-.TP
 .B \-n, --numerals
 Include at least one number in the password.  This is the default
 if the standard output is a tty device.
 .TP
-.B \--no-numerals
+.B \-y, --symbols
+Include at least one special character in the password. 
+.TP
+.B \-0, --no-numerals
 Don't include a number in the generated passwords.
 .TP
-.B \--no-capitalize
+.B \-A, --no-capitalize
 Don't bother to include any capital letters in the generated passwords.
 .TP
+.B \-C
+Print the generated passwords in columns.  This is the default if the 
+standard output is a tty device.
+.TP
 .B \-N, --num-passwords=\fInum
 Generate 
 .I num
-passwords.  This defaults to a screenful if passwords are 
-printed by columns, and one password.
+passwords. This defaults to a screenful if passwords are printed by columns.
 .TP
 .B \-s, --secure
 Generate completely random, hard-to-memorize paswords.  These should
@@ -71,6 +73,14 @@
 guaranteed that users will simply write the password on a piece of 
 paper taped to the monitor...
 .TP
+.B \-H, --sha1=\fI/path/to/file[#seed]
+Will use the sha1's hash of given file and the optional seed to create  
+password. It will allow you to compute the same password later, 
+if you remember the file, seed, and pwgen's options used.
+ie: pwgen -H ~/your_favourite.mp3#your@email.com gives 
+a list of possibles passwords for your pop3 account, and you can
+ask this list again and again.
+.TP
 .B \-h, --help
 Print a help message.
 .TP
diff -uN pwgen-2.03/pwgen.c pwgen-2.04/pwgen.c
--- pwgen-2.03/pwgen.c	2003-01-16 02:23:10.000000000 +0100
+++ pwgen-2.04/pwgen.c	2005-01-08 18:52:26.000000000 +0100
@@ -18,18 +18,25 @@
 
 #include "pwgen.h"
 
-struct pwgen_func generators[] = {
-	{ "phonemes",	pw_phonemes,	0 },
-	{ "rand",	pw_rand,	0 },
-	{ 0,		0,		0 }
-};
+/* Globals variables */
+int (*pw_number)(int max_num);
+
+const char *pw_digits = PW_STRDIGITS;
+const char *pw_uppers = PW_STRUPPERS;
+const char *pw_lowers = PW_STRLOWERS;
+const char *pw_symbols = PW_STRSYMBOLS;
+const char *pw_ambiguous = PW_STRAMBIGUOUS;
+
 
 /* Program parameters set via getopt */
 
 int	pw_length = 8;
 int	num_pw = -1;
-int	case_flag = 0;
-int	numeric_flag = 0;
+int	digits_flag = 0;
+int	uppers_flag = 0;
+int	lowers_flag = PW_LOWERS;
+int	symbols_flag = 0;
+int	ambiguous_flag = 0;
 int	do_columns = 0;
 
 #ifdef HAVE_GETOPT_LONG
@@ -37,40 +44,58 @@
 	{ "alt-phonics", no_argument, 0, 'a' },
 	{ "capitalize", no_argument, 0, 'c' },
 	{ "numerals", no_argument, 0, 'n'},
+	{ "symbols", no_argument, 0, 'y'},
 	{ "num-passwords", required_argument, 0, 'N'},
 	{ "secure", no_argument, 0, 's' },
 	{ "help", no_argument, 0, 'h'},
-	{ "no-numerals", no_argument, &numeric_flag, 0 },
-	{ "no-capitalize", no_argument, &case_flag, 0 },
+	{ "no-numerals", no_argument, 0, '0' },
+	{ "no-capitalize", no_argument, 0, 'A' },
+	{ "sha1", required_argument, 0, 'H' },
+	{ "ambiguous", no_argument, 0, 'B' },
 	{ 0, 0, 0, 0}
 };
 #endif
 
 const char *usage_msg =
-"Usage: pwgen [ OPTIONS ] [ pw_length ] [ num_pw ]\n\n"
-"Options supported by pwgen:\n"
-"  -c or -capitalize\n"
-"\tInclude at least one capital letter in the password\n"
-"  -n or --numerals\n"
-"\tInclude at least one number in the password\n"
-"  -s or --secure\n"
-"\tGenerate completely random passwords\n"	
-"  -h or --help\n"
-"\tPrint a help message\n"
-"  --no-numerals, --no-capitalize\n"
-"\tDon't include a number or capital letter in the password\n"
-"  -C\n\tPrint the generated passwords in columns\n"
-"  -1\n\tDon't print the generated passwords in columns\n"
+"Usage: pwgen [ OPTIONS ] [ pw_length ] [ num_pw ]\n\n\
+Options supported by pwgen:\n\
+  -c or -capitalize\n\
+\tInclude at least one capital letter in the password\n\
+  -n or --numerals\n\
+\tInclude at least one number in the password\n\
+  -y or --symbols\n\
+\tInclude at least one special symbols in the password\n\
+  -0 or --no-numerals\n\
+\tDon't include numbers in the password\n\
+  -A or --no-capitalize\n\
+\tDon't include a capitals letters in the password\n\
+  -B or --ambiguous\n\
+\tDon't include ambiguous characters in the password\n\
+  -s or --secure\n\
+\tGenerate completely random passwords\n\
+  -H or --sha1=path/to/file[#seed]\n\
+\tUse sha1 hash of given file as a (not so) random generator\n\
+  -C\n\tPrint the generated passwords in columns\n\
+  -1\n\tDon't print the generated passwords in columns\n\
+  -h or --help\n\
+\tPrint a help message\n"
 ;	
+
+const char *pw_getopt = "acny0ABsH:C1hN:";
 	
-static void usage(void)
+static void usage(void);
+int main (int argc, char **argv);
+
+static void usage()
 {
 	fprintf(stderr, usage_msg);
 	exit(1);
 }
 
 
-int main(int argc, char **argv)
+int main(argc, argv)
+	int argc;
+	char **argv;
 {
 	int	term_width = 80;
 	int	c, i;
@@ -79,28 +104,41 @@
 	void	(*pwgen)(char *inbuf, int size, int pw_flags);
 
 	pwgen = pw_phonemes;
+	pw_number = pw_random_number;
 	if (isatty(1)) {
 		do_columns = 1;
-		case_flag = PW_ONE_CASE;
-		numeric_flag = PW_ONE_NUMBER;
+		digits_flag = PW_DIGITS;
+		uppers_flag = PW_UPPERS;
 	}
 
 	while (1) {
 #ifdef HAVE_GETOPT_LONG
-		c = getopt_long(argc, argv, "1aCcnN:sh", pwgen_options, 0);
+		c = getopt_long(argc, argv, pw_getopt, pwgen_options, 0);
 #else
-		c = getopt(argc, argv, "1aCcnN:sh");
+		c = getopt(argc, argv, pw_getopt);
 #endif
 		if (c == -1)
 			break;
 		switch (c) {
 		case 'a':
 			break;
+		case 'b':
+			ambiguous_flag = PW_AMBIGUOUS;
+			break;
 		case 'c':
-			case_flag = PW_ONE_CASE;
+			uppers_flag = PW_UPPERS;
+			break;
+		case 'A':
+			uppers_flag = 0;
 			break;
 		case 'n':
-			numeric_flag = PW_ONE_NUMBER;
+			digits_flag = PW_DIGITS;
+			break;
+		case '0':
+			digits_flag = 0;
+			break;
+		case 'y':
+			symbols_flag = PW_SYMBOLS;
 			break;
 		case 'N':
 			num_pw = strtol(optarg, &tmp, 0);
@@ -120,6 +158,10 @@
 		case '1':
 			do_columns = 0;
 			break;
+		case 'H': 
+			pw_sha1_init(optarg);
+			pw_number = pw_sha1_number;
+			break;
 		case 'h':
 		case '?':
 			usage();
@@ -137,7 +179,6 @@
 		}
 		optind++;
 	}
-
 	if (optind < argc) {
 		num_pw = strtol(argv[optind], &tmp, 0);
 		if (*tmp) {
@@ -146,7 +187,7 @@
 			exit(1);
 		}
 	}
-	
+
 	if (do_columns) {
 		num_cols = term_width / (pw_length+1);
 		if (num_cols == 0)
@@ -161,7 +202,7 @@
 		exit(1);
 	}
 	for (i=0; i < num_pw; i++) {
-		pwgen(buf, pw_length, case_flag | numeric_flag);
+		pwgen(buf, pw_length, digits_flag | uppers_flag | lowers_flag | symbols_flag | ambiguous_flag);
 		if (!do_columns || ((i % num_cols) == (num_cols-1)))
 			printf("%s\n", buf);
 		else
diff -uN pwgen-2.03/pwgen.h pwgen-2.04/pwgen.h
--- pwgen-2.03/pwgen.h	2003-01-16 02:23:10.000000000 +0100
+++ pwgen-2.04/pwgen.h	2005-01-08 12:06:53.000000000 +0100
@@ -17,23 +17,37 @@
  */
 #define CONSONANT	0x0001
 #define VOWEL		0x0002
-#define DIPTHONG	0x0004
-#define NOT_FIRST	0x0008
+#define NOT_FIRST	0x0004
 
 /*
  * Flags for the pwgen function
  */
-#define PW_ONE_NUMBER	0x0001
-#define PW_ONE_CASE	0x0002
+#define PW_DIGITS	0x0001
+#define PW_UPPERS	0x0002
+#define PW_LOWERS	0x0004
+#define PW_SYMBOLS	0x0008
+#define PW_AMBIGUOUS	0x0010
+
+/*
+ * characters class
+ */
+#define PW_STRDIGITS	"0123456789"
+#define PW_STRUPPERS	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PW_STRLOWERS	"abcdefghijklmnopqrstuvwxyz"
+#define PW_STRSYMBOLS	"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define PW_STRAMBIGUOUS	"B8G6I1l0OQDS5Z2"
+extern const char *pw_digits; /* =PW_STRDIGITS */
+extern const char *pw_uppers; /* =PW_STRUPPERS */
+extern const char *pw_lowers; /* =PW_STRLOWERS */
+extern const char *pw_symbols; /* =PW_STRSYMBOLS */
+extern const char *pw_ambiguous; /* =PW_STRAMBIGUOUS */
 
-struct pwgen_func {
-	const char	*name;
-	void		(*func)(char *buf, int size, int pw_flags);
-	int		flags;
-};
 
 /* Function prototypes */
 
+/* pointer to choose between random or sha1 pseudo random number generator */
+extern int (*pw_number)(int max_num);
+
 /* pw_phonemes.c */
 extern void pw_phonemes(char *buf, int size, int pw_flags);
 
@@ -42,3 +56,7 @@
 
 /* randnum.c */
 extern int pw_random_number(int max_num);
+
+/* sha1num.c */
+extern void pw_sha1_init(char *sha1);
+extern int pw_sha1_number(int max_num);
diff -uN pwgen-2.03/randnum.c pwgen-2.04/randnum.c
--- pwgen-2.03/randnum.c	2003-01-16 02:17:51.000000000 +0100
+++ pwgen-2.04/randnum.c	2005-01-07 23:38:35.000000000 +0100
@@ -21,8 +21,10 @@
 extern double drand48(void);
 #endif
 
+static int get_random_fd(void);
+
 /* Borrowed/adapted from e2fsprogs's UUID generation code */
-static int get_random_fd(void)
+static int get_random_fd()
 {
 	struct timeval	tv;
 	static int	fd = -2;
@@ -56,7 +58,8 @@
  * Generate a random number n, where 0 <= n < max_num, using
  * /dev/urandom if possible.
  */
-int pw_random_number(int max_num)
+int pw_random_number(max_num)
+	int max_num;
 {
 	int i, fd = get_random_fd();
 	int lose_counter = 0, nbytes=4;
diff -uN pwgen-2.03/sha1.c pwgen-2.04/sha1.c
--- pwgen-2.03/sha1.c	1970-01-01 01:00:00.000000000 +0100
+++ pwgen-2.04/sha1.c	2005-01-07 23:35:12.000000000 +0100
@@ -0,0 +1,399 @@
+/*
+ *  FIPS-180-1 compliant SHA-1 implementation
+ *
+ *  Copyright (C) 2001-2003  Christophe Devine
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <string.h>
+
+#include "sha1.h"
+
+
+void sha1_process(sha1_context *ctx, uint8 data[64]);
+			
+
+#define GET_UINT32(n,b,i)                       \
+{                                               \
+    (n) = ( (uint32) (b)[(i)    ] << 24 )       \
+        | ( (uint32) (b)[(i) + 1] << 16 )       \
+        | ( (uint32) (b)[(i) + 2] <<  8 )       \
+        | ( (uint32) (b)[(i) + 3]       );      \
+}
+
+#define PUT_UINT32(n,b,i)                       \
+{                                               \
+    (b)[(i)    ] = (uint8) ( (n) >> 24 );       \
+    (b)[(i) + 1] = (uint8) ( (n) >> 16 );       \
+    (b)[(i) + 2] = (uint8) ( (n) >>  8 );       \
+    (b)[(i) + 3] = (uint8) ( (n)       );       \
+}
+
+void sha1_starts(ctx)
+	sha1_context *ctx;
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+    ctx->state[4] = 0xC3D2E1F0;
+}
+
+void sha1_process(ctx, data)
+	sha1_context *ctx;
+	uint8 data[64];
+{
+    uint32 temp, W[16], A, B, C, D, E;
+
+    GET_UINT32( W[0],  data,  0 );
+    GET_UINT32( W[1],  data,  4 );
+    GET_UINT32( W[2],  data,  8 );
+    GET_UINT32( W[3],  data, 12 );
+    GET_UINT32( W[4],  data, 16 );
+    GET_UINT32( W[5],  data, 20 );
+    GET_UINT32( W[6],  data, 24 );
+    GET_UINT32( W[7],  data, 28 );
+    GET_UINT32( W[8],  data, 32 );
+    GET_UINT32( W[9],  data, 36 );
+    GET_UINT32( W[10], data, 40 );
+    GET_UINT32( W[11], data, 44 );
+    GET_UINT32( W[12], data, 48 );
+    GET_UINT32( W[13], data, 52 );
+    GET_UINT32( W[14], data, 56 );
+    GET_UINT32( W[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define R(t)                                            \
+(                                                       \
+    temp = W[(t -  3) & 0x0F] ^ W[(t - 8) & 0x0F] ^     \
+           W[(t - 14) & 0x0F] ^ W[ t      & 0x0F],      \
+    ( W[t & 0x0F] = S(temp,1) )                         \
+)
+
+#define P(a,b,c,d,e,x)                                  \
+{                                                       \
+    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \
+}
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+    E = ctx->state[4];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+#define K 0x5A827999
+
+    P( A, B, C, D, E, W[0]  );
+    P( E, A, B, C, D, W[1]  );
+    P( D, E, A, B, C, W[2]  );
+    P( C, D, E, A, B, W[3]  );
+    P( B, C, D, E, A, W[4]  );
+    P( A, B, C, D, E, W[5]  );
+    P( E, A, B, C, D, W[6]  );
+    P( D, E, A, B, C, W[7]  );
+    P( C, D, E, A, B, W[8]  );
+    P( B, C, D, E, A, W[9]  );
+    P( A, B, C, D, E, W[10] );
+    P( E, A, B, C, D, W[11] );
+    P( D, E, A, B, C, W[12] );
+    P( C, D, E, A, B, W[13] );
+    P( B, C, D, E, A, W[14] );
+    P( A, B, C, D, E, W[15] );
+    P( E, A, B, C, D, R(16) );
+    P( D, E, A, B, C, R(17) );
+    P( C, D, E, A, B, R(18) );
+    P( B, C, D, E, A, R(19) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0x6ED9EBA1
+
+    P( A, B, C, D, E, R(20) );
+    P( E, A, B, C, D, R(21) );
+    P( D, E, A, B, C, R(22) );
+    P( C, D, E, A, B, R(23) );
+    P( B, C, D, E, A, R(24) );
+    P( A, B, C, D, E, R(25) );
+    P( E, A, B, C, D, R(26) );
+    P( D, E, A, B, C, R(27) );
+    P( C, D, E, A, B, R(28) );
+    P( B, C, D, E, A, R(29) );
+    P( A, B, C, D, E, R(30) );
+    P( E, A, B, C, D, R(31) );
+    P( D, E, A, B, C, R(32) );
+    P( C, D, E, A, B, R(33) );
+    P( B, C, D, E, A, R(34) );
+    P( A, B, C, D, E, R(35) );
+    P( E, A, B, C, D, R(36) );
+    P( D, E, A, B, C, R(37) );
+    P( C, D, E, A, B, R(38) );
+    P( B, C, D, E, A, R(39) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) ((x & y) | (z & (x | y)))
+#define K 0x8F1BBCDC
+
+    P( A, B, C, D, E, R(40) );
+    P( E, A, B, C, D, R(41) );
+    P( D, E, A, B, C, R(42) );
+    P( C, D, E, A, B, R(43) );
+    P( B, C, D, E, A, R(44) );
+    P( A, B, C, D, E, R(45) );
+    P( E, A, B, C, D, R(46) );
+    P( D, E, A, B, C, R(47) );
+    P( C, D, E, A, B, R(48) );
+    P( B, C, D, E, A, R(49) );
+    P( A, B, C, D, E, R(50) );
+    P( E, A, B, C, D, R(51) );
+    P( D, E, A, B, C, R(52) );
+    P( C, D, E, A, B, R(53) );
+    P( B, C, D, E, A, R(54) );
+    P( A, B, C, D, E, R(55) );
+    P( E, A, B, C, D, R(56) );
+    P( D, E, A, B, C, R(57) );
+    P( C, D, E, A, B, R(58) );
+    P( B, C, D, E, A, R(59) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0xCA62C1D6
+
+    P( A, B, C, D, E, R(60) );
+    P( E, A, B, C, D, R(61) );
+    P( D, E, A, B, C, R(62) );
+    P( C, D, E, A, B, R(63) );
+    P( B, C, D, E, A, R(64) );
+    P( A, B, C, D, E, R(65) );
+    P( E, A, B, C, D, R(66) );
+    P( D, E, A, B, C, R(67) );
+    P( C, D, E, A, B, R(68) );
+    P( B, C, D, E, A, R(69) );
+    P( A, B, C, D, E, R(70) );
+    P( E, A, B, C, D, R(71) );
+    P( D, E, A, B, C, R(72) );
+    P( C, D, E, A, B, R(73) );
+    P( B, C, D, E, A, R(74) );
+    P( A, B, C, D, E, R(75) );
+    P( E, A, B, C, D, R(76) );
+    P( D, E, A, B, C, R(77) );
+    P( C, D, E, A, B, R(78) );
+    P( B, C, D, E, A, R(79) );
+
+#undef K
+#undef F
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+    ctx->state[4] += E;
+}
+
+void sha1_update(ctx, input, length )
+	sha1_context *ctx;
+	uint8 *input;
+	uint32 length;
+{
+    uint32 left, fill;
+
+    if( ! length ) return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += length;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < length )
+        ctx->total[1]++;
+
+    if( left && length >= fill )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, fill );
+        sha1_process( ctx, ctx->buffer );
+        length -= fill;
+        input  += fill;
+        left = 0;
+    }
+
+    while( length >= 64 )
+    {
+        sha1_process( ctx, input );
+        length -= 64;
+        input  += 64;
+    }
+
+    if( length )
+    {
+        memcpy( (void *) (ctx->buffer + left),
+                (void *) input, length );
+    }
+}
+
+static uint8 sha1_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void sha1_finish( ctx, digest )
+	sha1_context *ctx;
+	uint8 digest[20];
+{
+    uint32 last, padn;
+    uint32 high, low;
+    uint8 msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_UINT32( high, msglen, 0 );
+    PUT_UINT32( low,  msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    sha1_update( ctx, sha1_padding, padn );
+    sha1_update( ctx, msglen, 8 );
+
+    PUT_UINT32( ctx->state[0], digest,  0 );
+    PUT_UINT32( ctx->state[1], digest,  4 );
+    PUT_UINT32( ctx->state[2], digest,  8 );
+    PUT_UINT32( ctx->state[3], digest, 12 );
+    PUT_UINT32( ctx->state[4], digest, 16 );
+}
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * those are the standard FIPS-180-1 test vectors
+ */
+
+static char *msg[] = 
+{
+    "abc",
+    "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+    NULL
+};
+
+static char *val[] =
+{
+    "a9993e364706816aba3e25717850c26c9cd0d89d",
+    "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+    "34aa973cd4c4daa4f61eeb2bdbad27316534016f"
+};
+
+int main( argc, argv )
+	int argc;
+	char **argv;
+{
+    FILE *f;
+    int i, j;
+    char output[41];
+    sha1_context ctx;
+    unsigned char buf[1000];
+    unsigned char sha1sum[20];
+
+    if( argc < 2 )
+    {
+        printf( "\n SHA-1 Validation Tests:\n\n" );
+
+        for( i = 0; i < 3; i++ )
+        {
+            printf( " Test %d ", i + 1 );
+
+            sha1_starts( &ctx );
+
+            if( i < 2 )
+            {
+                sha1_update( &ctx, (uint8 *) msg[i],
+                             strlen( msg[i] ) );
+            }
+            else
+            {
+                memset( buf, 'a', 1000 );
+
+                for( j = 0; j < 1000; j++ )
+                {
+                    sha1_update( &ctx, (uint8 *) buf, 1000 );
+                }
+            }
+
+            sha1_finish( &ctx, sha1sum );
+
+            for( j = 0; j < 20; j++ )
+            {
+                sprintf( output + j * 2, "%02x", sha1sum[j] );
+            }
+
+            if( memcmp( output, val[i], 40 ) )
+            {
+                printf( "failed!\n" );
+                return( 1 );
+            }
+
+            printf( "passed.\n" );
+        }
+
+        printf( "\n" );
+    }
+    else
+    {
+        if( ! ( f = fopen( argv[1], "rb" ) ) )
+        {
+            perror( "fopen" );
+            return( 1 );
+        }
+
+        sha1_starts( &ctx );
+
+        while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
+        {
+            sha1_update( &ctx, buf, i );
+        }
+
+        sha1_finish( &ctx, sha1sum );
+
+        for( j = 0; j < 20; j++ )
+        {
+            printf( "%02x", sha1sum[j] );
+        }
+
+        printf( "  %s\n", argv[1] );
+    }
+
+    return( 0 );
+}
+
+#endif
diff -uN pwgen-2.03/sha1.h pwgen-2.04/sha1.h
--- pwgen-2.03/sha1.h	1970-01-01 01:00:00.000000000 +0100
+++ pwgen-2.04/sha1.h	2005-01-07 02:49:36.000000000 +0100
@@ -0,0 +1,24 @@
+#ifndef _SHA1_H
+#define _SHA1_H
+
+#ifndef uint8
+#define uint8  unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+    uint32 total[2];
+    uint32 state[5];
+    uint8 buffer[64];
+}
+sha1_context;
+
+void sha1_starts( sha1_context *ctx );
+void sha1_update( sha1_context *ctx, uint8 *input, uint32 length );
+void sha1_finish( sha1_context *ctx, uint8 digest[20] );
+
+#endif /* sha1.h */
diff -uN pwgen-2.03/sha1num.c pwgen-2.04/sha1num.c
--- pwgen-2.03/sha1num.c	1970-01-01 01:00:00.000000000 +0100
+++ pwgen-2.04/sha1num.c	2005-01-08 19:52:28.000000000 +0100
@@ -0,0 +1,77 @@
+/*
+ * sha1num.c --- generate sha1 hash based, pseudo random numbers
+ *
+ * Copyright (C) 2005 by Olivier Guerrier
+ *
+ * This file may be distributed under the terms of the GNU Public
+ * License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "pwgen.h"
+#include "sha1.h"
+
+sha1_context sha1_ctx;
+char *sha1_seed;
+const char *sha1_magic="pwgen";
+unsigned char sha1sum[20];
+int sha1sum_idx=20;
+	
+void pw_sha1_init(sha1)
+	char *sha1;
+{
+        int i = 0;
+	char *seed;
+	FILE *f;
+        unsigned char buf[1024];
+
+	if ((seed = strchr(sha1,'#'))) {
+		*(seed++) = 0;
+		sha1_seed = malloc(strlen(seed)+1);
+	        if (!sha1_seed) {
+	                fprintf(stderr, "Couldn't malloc sha1_seed buffer.\n");
+	                exit(1);
+	        }
+		strcpy(sha1_seed, seed);
+	}
+	else {
+		sha1_seed = malloc(strlen(sha1_magic)+1);
+	        if (!sha1_seed) {
+	                fprintf(stderr, "Couldn't malloc sha1_seed buffer.\n");
+	                exit(1);
+	        }
+		strcpy(sha1_seed, sha1_magic);
+	}
+
+	if( ! ( f = fopen( sha1, "rb" ) ) ) {
+	        fprintf(stderr, "Couldn't open file: %s.\n", sha1);
+		exit(1);
+	}
+
+	sha1_starts( &sha1_ctx );                                   
+	while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) {
+		sha1_update( &sha1_ctx, buf, i );
+	}
+
+	return;
+}
+
+
+int pw_sha1_number(max_num)
+	int max_num;
+{
+	int val;
+	sha1_context ctx;
+	
+	if(sha1sum_idx>19) 
+	{
+		sha1sum_idx = 0;
+		sha1_update(&sha1_ctx, sha1_seed, strlen(sha1_seed));
+		ctx = sha1_ctx;
+		sha1_finish(&ctx, sha1sum );
+	}
+	val = (int) (sha1sum[sha1sum_idx++] / ((float) 256) * max_num);
+	return (val);
+}
