├── tests ├── err │ ├── 0 │ ├── 1 │ ├── 2 │ ├── 3 │ ├── 4 │ ├── 5 │ ├── 6 │ ├── 7 │ ├── 8 │ ├── 9 │ ├── 10 │ ├── 11 │ ├── 12 │ ├── 13 │ ├── 14 │ ├── 15 │ ├── 16 │ ├── 17 │ └── 18 ├── out │ ├── 1 │ ├── 2 │ ├── 3 │ ├── 4 │ ├── 5 │ ├── 6 │ ├── 7 │ ├── 8 │ ├── 9 │ ├── 10 │ ├── 11 │ ├── 12 │ ├── 13 │ ├── 14 │ ├── 15 │ ├── 16 │ ├── 17 │ ├── 18 │ └── 19 ├── args │ ├── 6 │ └── 7 ├── .gitignore ├── in │ ├── 1 │ ├── 2 │ ├── 3 │ ├── 4 │ ├── 5 │ ├── 6 │ ├── 7 │ ├── 8 │ ├── 9 │ ├── 10 │ ├── 11 │ ├── 12 │ ├── 13 │ ├── 14 │ ├── 15 │ ├── 16 │ ├── 17 │ ├── 18 │ └── 19 ├── gen_out.sh └── run.sh ├── gen.cpp ├── .gitignore ├── CMakeLists.txt ├── src ├── sc_expandpass.cpp ├── validate.h ├── validate.cpp ├── parse.h ├── expand.h ├── sc_expandpass.h ├── util.cpp ├── expansion.h ├── util.h ├── expandpass.cpp ├── parse.cpp ├── expansion.cpp └── expand.cpp ├── seed.txt ├── makefile ├── example_seeds.txt ├── todo └── README.md /tests/err/1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/10: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/11: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/12: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/13: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/14: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/15: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/16: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/17: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/18: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/3: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/4: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/7: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/err/8: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/out/5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/out/9: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/args/6: -------------------------------------------------------------------------------- 1 | --unquoted 2 | -------------------------------------------------------------------------------- /tests/args/7: -------------------------------------------------------------------------------- 1 | --normalize 2 | -------------------------------------------------------------------------------- /tests/out/19: -------------------------------------------------------------------------------- 1 | ab 2 | ub 3 | a1 4 | a2 5 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | test_stdout 2 | test_stderr 3 | -------------------------------------------------------------------------------- /tests/err/0: -------------------------------------------------------------------------------- 1 | Error opening seed file: in/0 2 | -------------------------------------------------------------------------------- /tests/out/15: -------------------------------------------------------------------------------- 1 | MaryMichael 2 | marymichael 3 | -------------------------------------------------------------------------------- /tests/in/8: -------------------------------------------------------------------------------- 1 | # copy string 2 | "Hello"[ - c2 "" i1c3 "abcdefg" ] 3 | -------------------------------------------------------------------------------- /tests/out/10: -------------------------------------------------------------------------------- 1 | CommonAUniqueB 2 | UniqueACommonB 3 | CommonACommonB 4 | -------------------------------------------------------------------------------- /tests/out/11: -------------------------------------------------------------------------------- 1 | UniqueACommonB 2 | CommonACommonB 3 | CommonAUniqueB 4 | -------------------------------------------------------------------------------- /tests/out/12: -------------------------------------------------------------------------------- 1 | CommonACommonB 2 | UniqueACommonB 3 | CommonAUniqueB 4 | -------------------------------------------------------------------------------- /tests/in/18: -------------------------------------------------------------------------------- 1 | #Null-or-space pattern 2 | {-" "}"TE"{-" "}"ST"{-" "} 3 | 4 | -------------------------------------------------------------------------------- /tests/out/14: -------------------------------------------------------------------------------- 1 | AD 2 | BCD 3 | BUD 4 | AEF 5 | BCEF 6 | BUEF 7 | AEU 8 | BCEU 9 | -------------------------------------------------------------------------------- /tests/err/5: -------------------------------------------------------------------------------- 1 | ERROR: Invalid characters within option 2 | line 6 ( <{eE3}"th"> ) 3 | -------------------------------------------------------------------------------- /tests/in/15: -------------------------------------------------------------------------------- 1 | #group tags 2 | < 3 | {"m"}"ary" 4 | {"m"}"ichael" 5 | > 6 | -------------------------------------------------------------------------------- /tests/in/19: -------------------------------------------------------------------------------- 1 | #modified unique 2 | < 3 | {"a"} 4 | {"b"} 5 | > 6 | -------------------------------------------------------------------------------- /tests/out/18: -------------------------------------------------------------------------------- 1 | TEST 2 | TEST 3 | TE ST 4 | TE ST 5 | TEST 6 | TEST 7 | TE ST 8 | TE ST 9 | -------------------------------------------------------------------------------- /tests/in/9: -------------------------------------------------------------------------------- 1 | # should fail because of poor formatting 2 | < { > "A" >blah it doesn't matter what I type here } "B" > 3 | 4 | -------------------------------------------------------------------------------- /tests/in/10: -------------------------------------------------------------------------------- 1 | #unique tag 2 | { 3 | 4 | "CommonA" 5 | } 6 | { 7 | 8 | "CommonB" 9 | } 10 | -------------------------------------------------------------------------------- /tests/err/9: -------------------------------------------------------------------------------- 1 | ERROR: Invalid characters within option 2 | line 2 ( < { > "A" >blah it doesn't matter what I type here } "B" > ) 3 | -------------------------------------------------------------------------------- /tests/in/16: -------------------------------------------------------------------------------- 1 | #many group tags 2 | < 3 | <{-"g"}"roup"{"b"}"loop"> 4 | <{-"g"}"roup"{"b"}"loop"> 5 | > 6 | -------------------------------------------------------------------------------- /tests/in/2: -------------------------------------------------------------------------------- 1 | # seed formatted on one line 2 | ( "Jean" < { "M" "m" } "arry" { "M" "m" } "e" >) "123" [-s1 "0123456789"] { "!" "!!" } 3 | -------------------------------------------------------------------------------- /tests/in/11: -------------------------------------------------------------------------------- 1 | #unique tag (different order) 2 | { 3 | 4 | "CommonA" 5 | } 6 | { 7 | "CommonB" 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tests/in/12: -------------------------------------------------------------------------------- 1 | #unique tag (different order) 2 | { 3 | "CommonA" 4 | 5 | } 6 | { 7 | "CommonB" 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tests/in/14: -------------------------------------------------------------------------------- 1 | #unique children percolate up 2 | { 3 | "A" 4 | <"B"{"C"}> 5 | } 6 | { 7 | "D" 8 | <"E"{"F"}> 9 | } 10 | -------------------------------------------------------------------------------- /tests/out/13: -------------------------------------------------------------------------------- 1 | ADU 2 | CDU 3 | ADE 4 | BUDE 5 | UBDE 6 | CDE 7 | UDE 8 | AUD 9 | CUD 10 | AED 11 | BUED 12 | UBED 13 | CED 14 | UED 15 | AU 16 | CU 17 | -------------------------------------------------------------------------------- /tests/out/17: -------------------------------------------------------------------------------- 1 | PG 2 | pg 3 | PeteGeorge 4 | peteGeorge 5 | PeterGeorge 6 | peterGeorge 7 | Petegeorge 8 | petegeorge 9 | Petergeorge 10 | petergeorge 11 | -------------------------------------------------------------------------------- /tests/in/17: -------------------------------------------------------------------------------- 1 | #Multiple gtags for single option group 2 | < 3 | <{}> 4 | <{<{"J""j"}"erod">}> 5 | > 6 | 7 | -------------------------------------------------------------------------------- /gen.cpp: -------------------------------------------------------------------------------- 1 | #include "src/util.cpp" 2 | #include "src/expansion.cpp" 3 | #include "src/parse.cpp" 4 | #include "src/expand.cpp" 5 | #include "src/validate.cpp" 6 | #include "src/expandpass.cpp" 7 | 8 | -------------------------------------------------------------------------------- /tests/in/1: -------------------------------------------------------------------------------- 1 | # reasonable seed 2 | ( 3 | "Jean" 4 | < 5 | { "M" "m" } "arry" { "M" "m" } "e" 6 | > 7 | ) 8 | "123" 9 | [-s1 "0123456789"] 10 | { 11 | "!" 12 | "!!" 13 | } 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gdb_history 2 | .vscode 3 | .vs 4 | *.10x 5 | .DS_Store 6 | tags 7 | expandpass 8 | expandpass.dSYM 9 | password.txt 10 | build 11 | *.exe 12 | *.obj 13 | *.ilk 14 | *.dbg 15 | *.pdb 16 | 17 | -------------------------------------------------------------------------------- /tests/in/4: -------------------------------------------------------------------------------- 1 | # - null strings 2 | { 3 | "ETH" 4 | "3TH" 5 | < 6 | <{"e""E""3"}"th"> 7 | { 8 | - 9 | < 10 | <{"e""E""3"}"r"> 11 | { 12 | - 13 | <{"e""3""i"}"um"> 14 | } 15 | > 16 | } 17 | > 18 | } 19 | -------------------------------------------------------------------------------- /tests/out/16: -------------------------------------------------------------------------------- 1 | roupBlooproupBloop 2 | groupBlooproupBloop 3 | GroupBloopGroupBloop 4 | roupBloopgroupBloop 5 | groupBloopgroupBloop 6 | roupblooproupbloop 7 | groupblooproupbloop 8 | GroupbloopGroupbloop 9 | roupbloopgroupbloop 10 | groupbloopgroupbloop 11 | -------------------------------------------------------------------------------- /tests/in/13: -------------------------------------------------------------------------------- 1 | #complicated, tiered unique tags 2 | { 3 | "A" 4 | ( 5 | <"B"> 6 | 7 | ) 8 | <"C"> 9 | 10 | } 11 | { 12 | ( 13 | "D" 14 | { 15 | 16 | <"E"> 17 | } 18 | ) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tests/in/5: -------------------------------------------------------------------------------- 1 | # single-character strings (will fail w/o --unquoted) 2 | { 3 | "ETH" 4 | "3TH" 5 | < 6 | <{eE3}"th"> 7 | { 8 | - 9 | < 10 | <{eE3}r> 11 | { 12 | - 13 | <{e3i}"um"> 14 | } 15 | > 16 | } 17 | > 18 | } 19 | -------------------------------------------------------------------------------- /tests/in/6: -------------------------------------------------------------------------------- 1 | # single-character strings (will succeed w/ --unquoted) 2 | { 3 | "ETH" 4 | "3TH" 5 | < 6 | <{eE3}"th"> 7 | { 8 | - 9 | < 10 | <{eE3}r> 11 | { 12 | - 13 | <{e3i}"um"> 14 | } 15 | > 16 | } 17 | > 18 | } 19 | -------------------------------------------------------------------------------- /tests/in/3: -------------------------------------------------------------------------------- 1 | # base of seed that will get simplified 2 | { 3 | "ETH" 4 | "3TH" 5 | < 6 | <{"e""E""3"}"th"> 7 | { 8 | "" 9 | < 10 | <{"e""E""3"}"r"> 11 | { 12 | "" 13 | <{"e""3""i"}"um"> 14 | } 15 | > 16 | } 17 | > 18 | } 19 | -------------------------------------------------------------------------------- /tests/in/7: -------------------------------------------------------------------------------- 1 | # base of seed that will get simplified 2 | { 3 | "ETH" 4 | "3TH" 5 | < 6 | <{"e""E""3"}"th"> 7 | { 8 | "" 9 | < 10 | <{"e""E""3"}"r"> 11 | { 12 | "" 13 | <{"e""3""i"}"um"> 14 | } 15 | > 16 | } 17 | > 18 | } 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | 3 | project(Expandpass LANGUAGES C CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 9 | if(MSVC) 10 | add_compile_options(/FC) 11 | endif() 12 | 13 | set(SOURCE_FILES gen.cpp) 14 | add_executable(expandpass ${SOURCE_FILES}) 15 | 16 | -------------------------------------------------------------------------------- /src/sc_expandpass.cpp: -------------------------------------------------------------------------------- 1 | #ifndef SC_EXPANDPASS_H 2 | #define SC_EXPANDPASS_H 3 | 4 | struct sc_expandpass_ini 5 | { 6 | int state; 7 | const char *seed_file; 8 | }; 9 | 10 | void sc_expandpass_init(sc_expandpass_ini ini); 11 | int sc_expandpass_keyspace(); 12 | void sc_expandpass_seek(int state); 13 | void sc_expandpass_next(); 14 | void sc_expandpass_shutdown(); 15 | 16 | #endif //SC_EXPANDPASS_H 17 | 18 | -------------------------------------------------------------------------------- /tests/out/3: -------------------------------------------------------------------------------- 1 | ETH 2 | 3TH 3 | eth 4 | Eth 5 | 3th 6 | ether 7 | Ether 8 | 3ther 9 | ethEr 10 | EthEr 11 | 3thEr 12 | eth3r 13 | Eth3r 14 | 3th3r 15 | ethereum 16 | Ethereum 17 | 3thereum 18 | ethEreum 19 | EthEreum 20 | 3thEreum 21 | eth3reum 22 | Eth3reum 23 | 3th3reum 24 | ether3um 25 | Ether3um 26 | 3ther3um 27 | ethEr3um 28 | EthEr3um 29 | 3thEr3um 30 | eth3r3um 31 | Eth3r3um 32 | 3th3r3um 33 | etherium 34 | Etherium 35 | 3therium 36 | ethErium 37 | EthErium 38 | 3thErium 39 | eth3rium 40 | Eth3rium 41 | 3th3rium 42 | -------------------------------------------------------------------------------- /tests/out/4: -------------------------------------------------------------------------------- 1 | ETH 2 | 3TH 3 | eth 4 | Eth 5 | 3th 6 | ether 7 | Ether 8 | 3ther 9 | ethEr 10 | EthEr 11 | 3thEr 12 | eth3r 13 | Eth3r 14 | 3th3r 15 | ethereum 16 | Ethereum 17 | 3thereum 18 | ethEreum 19 | EthEreum 20 | 3thEreum 21 | eth3reum 22 | Eth3reum 23 | 3th3reum 24 | ether3um 25 | Ether3um 26 | 3ther3um 27 | ethEr3um 28 | EthEr3um 29 | 3thEr3um 30 | eth3r3um 31 | Eth3r3um 32 | 3th3r3um 33 | etherium 34 | Etherium 35 | 3therium 36 | ethErium 37 | EthErium 38 | 3thErium 39 | eth3rium 40 | Eth3rium 41 | 3th3rium 42 | -------------------------------------------------------------------------------- /tests/out/6: -------------------------------------------------------------------------------- 1 | ETH 2 | 3TH 3 | eth 4 | Eth 5 | 3th 6 | ether 7 | Ether 8 | 3ther 9 | ethEr 10 | EthEr 11 | 3thEr 12 | eth3r 13 | Eth3r 14 | 3th3r 15 | ethereum 16 | Ethereum 17 | 3thereum 18 | ethEreum 19 | EthEreum 20 | 3thEreum 21 | eth3reum 22 | Eth3reum 23 | 3th3reum 24 | ether3um 25 | Ether3um 26 | 3ther3um 27 | ethEr3um 28 | EthEr3um 29 | 3thEr3um 30 | eth3r3um 31 | Eth3r3um 32 | 3th3r3um 33 | etherium 34 | Etherium 35 | 3therium 36 | ethErium 37 | EthErium 38 | 3thErium 39 | eth3rium 40 | Eth3rium 41 | 3th3rium 42 | -------------------------------------------------------------------------------- /seed.txt: -------------------------------------------------------------------------------- 1 | #Example Seed 2 | 3 | ( # '(' tries all orderings of its contents 4 | "Jean" 5 | < # '<' will concatenate its contents 6 | { "M" "m" } "arry" { "M" "m" } "e" 7 | > 8 | ) 9 | "123" 10 | [-s1 "0123456789"] # modification applies to "123". the '-' means "try without modification", the 's1' means "substitute one character" 11 | { # '{' tries each of its contents independently 12 | - # '-' is shorthand for the empty string (identical to "") 13 | "!" 14 | "!!" 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/validate.h: -------------------------------------------------------------------------------- 1 | #ifndef VALIDATE_H 2 | #define VALIDATE_H 3 | 4 | struct validation 5 | { 6 | int alpha; 7 | int up_alpha; 8 | int low_alpha; 9 | int numeric; 10 | int alphanumeric; 11 | int non_alphanumeric; 12 | int length_min; 13 | int length_max; 14 | }; 15 | void zero_validation(validation *v) 16 | { 17 | v->alpha = 0; 18 | v->up_alpha = 0; 19 | v->low_alpha = 0; 20 | v->numeric = 0; 21 | v->alphanumeric = 0; 22 | v->non_alphanumeric = 0; 23 | v->length_min = 0; 24 | v->length_max = max_pass_len; 25 | } 26 | 27 | int validate(validation *v, char *passholder, int len); 28 | 29 | #endif //VALIDATE_H 30 | -------------------------------------------------------------------------------- /tests/out/7: -------------------------------------------------------------------------------- 1 | { 2 | "ETH" 3 | "3TH" 4 | "eth" 5 | "Eth" 6 | "3th" 7 | "ether" 8 | "Ether" 9 | "3ther" 10 | "ethEr" 11 | "EthEr" 12 | "3thEr" 13 | "eth3r" 14 | "Eth3r" 15 | "3th3r" 16 | "ethereum" 17 | "Ethereum" 18 | "3thereum" 19 | "ethEreum" 20 | "EthEreum" 21 | "3thEreum" 22 | "eth3reum" 23 | "Eth3reum" 24 | "3th3reum" 25 | "ether3um" 26 | "Ether3um" 27 | "3ther3um" 28 | "ethEr3um" 29 | "EthEr3um" 30 | "3thEr3um" 31 | "eth3r3um" 32 | "Eth3r3um" 33 | "3th3r3um" 34 | "etherium" 35 | "Etherium" 36 | "3therium" 37 | "ethErium" 38 | "EthErium" 39 | "3thErium" 40 | "eth3rium" 41 | "Eth3rium" 42 | "3th3rium" 43 | } 44 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | EXE=expandpass 2 | CSRC=gen.cpp 3 | SRCDIR=src 4 | DEP=$(SRCDIR)/util.cpp $(SRCDIR)/util.cpp $(SRCDIR)/expansion.cpp $(SRCDIR)/parse.cpp $(SRCDIR)/expand.cpp $(SRCDIR)/validate.cpp $(SRCDIR)/expandpass.cpp $(CSRC) 5 | NOWARN=-Wno-write-strings 6 | CFLAGS=$(NOWARN)# -O3 7 | ARGS= 8 | 9 | make: $(EXE) 10 | 11 | 12 | run: password.txt 13 | 14 | 15 | frun: 16 | ./$(EXE) $(ARGS) 17 | 18 | $(EXE).dSYM: $(DEP) 19 | gcc $(CFLAGS) -ggdb3 $(CSRC) -o $(EXE) 20 | 21 | builddebug: $(EXE).dSYM 22 | 23 | 24 | debug: $(EXE).dSYM 25 | lldb -- ./$(EXE) $(ARGS) 26 | # gdb --args ./$(EXE) $(ARGS) 27 | 28 | $(EXE): $(DEP) seed.txt 29 | gcc $(CFLAGS) $(CSRC) -o $(EXE) 30 | 31 | password.txt: $(EXE) 32 | ./$(EXE) $(ARGS) 33 | 34 | test: $(EXE) 35 | tests/run.sh 36 | 37 | tags: $(DEP) 38 | ctags ./* 39 | 40 | clean: 41 | if [ -f $(EXE) ]; then rm $(EXE); fi 42 | if [ -d $(EXE).dSYM ]; then rm -r $(EXE).dSYM; fi 43 | 44 | replace: $(EXE) 45 | cp $(EXE) `which $(EXE)` 46 | 47 | -------------------------------------------------------------------------------- /tests/out/8: -------------------------------------------------------------------------------- 1 | Hello 2 | HelloHello 3 | aHelloaHelloaHello 4 | bHellobHellobHello 5 | cHellocHellocHello 6 | dHellodHellodHello 7 | eHelloeHelloeHello 8 | fHellofHellofHello 9 | gHellogHellogHello 10 | HaelloHaelloHaello 11 | HbelloHbelloHbello 12 | HcelloHcelloHcello 13 | HdelloHdelloHdello 14 | HeelloHeelloHeello 15 | HfelloHfelloHfello 16 | HgelloHgelloHgello 17 | HealloHealloHeallo 18 | HeblloHeblloHebllo 19 | HeclloHeclloHecllo 20 | HedlloHedlloHedllo 21 | HeelloHeelloHeello 22 | HeflloHeflloHefllo 23 | HeglloHeglloHegllo 24 | HelaloHelaloHelalo 25 | HelbloHelbloHelblo 26 | HelcloHelcloHelclo 27 | HeldloHeldloHeldlo 28 | HeleloHeleloHelelo 29 | HelfloHelfloHelflo 30 | HelgloHelgloHelglo 31 | HellaoHellaoHellao 32 | HellboHellboHellbo 33 | HellcoHellcoHellco 34 | HelldoHelldoHelldo 35 | HelleoHelleoHelleo 36 | HellfoHellfoHellfo 37 | HellgoHellgoHellgo 38 | HelloaHelloaHelloa 39 | HellobHellobHellob 40 | HellocHellocHelloc 41 | HellodHellodHellod 42 | HelloeHelloeHelloe 43 | HellofHellofHellof 44 | HellogHellogHellog 45 | -------------------------------------------------------------------------------- /src/validate.cpp: -------------------------------------------------------------------------------- 1 | #include "validate.h" 2 | 3 | int validate(validation *v, char *passholder, int len) 4 | { 5 | if(v->length_min <= len && v->length_max >= len) 6 | { 7 | int alpha_e = 0; 8 | int up_alpha_e = 0; 9 | int low_alpha_e = 0; 10 | int numeric_e = 0; 11 | int alphanumeric_e = 0; 12 | int non_alphanumeric_e = 0; 13 | while(*passholder != '\0') 14 | { 15 | if(*passholder >= 'A' && *passholder <= 'Z') { up_alpha_e++; alpha_e++; alphanumeric_e++; } 16 | else if(*passholder >= 'a' && *passholder <= 'z') { low_alpha_e++; alpha_e++; alphanumeric_e++; } 17 | else if(*passholder >= '0' && *passholder <= '9') { numeric_e++; alphanumeric_e++; } 18 | else { non_alphanumeric_e++; } 19 | passholder++; 20 | } 21 | if( 22 | alpha_e >= v->alpha && 23 | up_alpha_e >= v->up_alpha && 24 | low_alpha_e >= v->low_alpha && 25 | numeric_e >= v->numeric && 26 | alphanumeric_e >= v->alphanumeric && 27 | non_alphanumeric_e >= v->non_alphanumeric 28 | ) 29 | return 1; 30 | } 31 | return false; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/gen_out.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # DO NOT RUN THIS PRIOR TO ./run.sh 4 | # that would defeat the whole purpose 5 | # this is a helper function for quickly 6 | # generating tests ::when you are already 7 | # confident in the current build's functionality:: 8 | # (and will still need manual verification) 9 | 10 | cd `dirname $0` 11 | if [ ! -f ../expandpass ]; then echo "executable expandpass not found (expected `pwd`/../expandpass)" 1>&2; exit 1; fi 12 | 13 | #silly way to find out # tests 14 | max_tests=100 15 | n_tests=0 16 | for i in `eval "echo {1..$max_tests}"`; do 17 | if [ -f $i ] || [ -f in/$i ] || [ -f args/$i ] || [ -f out/$i ]; then 18 | n_tests=$i 19 | fi 20 | done 21 | 22 | for i in `eval "echo {1..$n_tests}"`; do 23 | if [ -f $i ]; then 24 | echo "Skip $i... (custom format)" 25 | else 26 | echo Gen $i... 27 | if [ -f args/$i ]; then 28 | cat args/$i | xargs ../expandpass -i "in/$i" > "out/$i" 2> "err/$i" 29 | else 30 | ../expandpass -i "in/$i" > "out/$i" 2> "err/$i" 31 | fi 32 | fi 33 | done 34 | 35 | assert_identical() 36 | { 37 | if ! diff "out/$1" "out/$2"; then 38 | echo "Genned output $1 and $2 differ" 39 | fi 40 | } 41 | 42 | #hardcoded but whatever 43 | assert_identical 1 2 44 | assert_identical 3 4 45 | assert_identical 4 6 46 | 47 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # runs files named "1" through "$n_tests" 4 | # if no file exists, assumes there's a corresponding 5 | # seed file in "in", and an 6 | # args fine in "args", and an 7 | # expected out file in "out" 8 | # expected err file in "err" 9 | 10 | cd `dirname $0` 11 | if [ ! -f ../expandpass ]; then echo "executable expandpass not found (expected `pwd`/../expandpass)" 1>&2; exit 1; fi 12 | 13 | ../expandpass --version 14 | 15 | #silly way to find out # tests 16 | max_tests=100 17 | n_tests=0 18 | for i in `eval "echo {1..$max_tests}"`; do 19 | if [ -f $i ] || [ -f in/$i ] || [ -f args/$i ] || [ -f out/$i ]; then 20 | n_tests=$i 21 | fi 22 | done 23 | 24 | for i in `eval "echo {1..$n_tests}"`; do 25 | echo -n Test $i... 26 | touch "in/$i" 27 | touch "out/$i" 28 | touch "err/$i" 29 | if [ -f $i ]; then 30 | if [ ./$i ]; then echo Passed 31 | else echo Failed 32 | fi 33 | else 34 | if [ -f args/$i ]; then 35 | cat args/$i | xargs ../expandpass -i "in/$i" > "test_stdout" 2> test_stderr 36 | else 37 | ../expandpass -i "in/$i" > "test_stdout" 2> test_stderr 38 | fi 39 | if ! diff test_stdout out/$i; then echo "Failed (stdout)"; exit 1 40 | elif ! diff test_stderr err/$i; then echo "Failed (stderr)"; exit 1 41 | else echo Passed 42 | fi 43 | rm test_stdout 44 | rm test_stderr 45 | fi 46 | done 47 | 48 | -------------------------------------------------------------------------------- /src/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSE_H 2 | #define PARSE_H 3 | 4 | #include "expand.h" 5 | 6 | const int max_read_line_len = 1024*10; 7 | 8 | const int ERROR_NULL = 0; 9 | const int ERROR_EOF = 1; 10 | const int ERROR_BADEOF = 2; 11 | const int ERROR_INVALID_LINE = 3; 12 | const int ERROR_INVALID_STRING = 4; 13 | const int ERROR_INVALID_MODIFICATION = 5; 14 | const int ERROR_UNTERMINATED_STRING = 6; 15 | const int ERROR_UNPARENTED_MODIFICATION = 7; 16 | const int ERROR_INVALID_NULL_MODIFICATION = 8; 17 | const int ERROR_MODIFICATION_EMPTY_GAMUT = 9; 18 | const int ERROR_NULL_CHILD = 10; 19 | const int ERROR_TAG_RANGE = 11; 20 | const int ERROR_TAG_SPECIFY = 12; 21 | 22 | struct parse_error 23 | { 24 | int error; 25 | int force; 26 | char *txt; 27 | }; 28 | 29 | int parse_modification(FILE *fp, int *line_n, char *buff, char **b, modification *m, parse_error *er); 30 | int parse_modifications(FILE *fp, int *line_n, char *buff, char **b, group *g, parse_error *er); 31 | int parse_child(FILE *fp, int unquoted, int *line_n, char *buff, char **b, group *g, group *prev_g, int depth, parse_error *er); 32 | int parse_childs(FILE *fp, int unquoted, int *line_n, char *buff, char **b, group *g, int depth, parse_error *er); 33 | int parse_tag(FILE *fp, int *line_n, char *buff, char **b, tag *t, int u, parse_error *er); 34 | group *parse(FILE *fp, int unquoted); 35 | void print_tag(tag t, int u); 36 | 37 | #endif //PARSE_H 38 | -------------------------------------------------------------------------------- /src/expand.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPAND_H 2 | #define EXPAND_H 3 | 4 | #include "expansion.h" 5 | 6 | char smart_sub(int i, char key); 7 | void basic_smart_substitute(int i, int sub_i, char *s); 8 | tag stamp_tag(tag t, tag dst, int inc); 9 | tag *clone_tag(tag src, tag *t); 10 | void stamp_tag_map(tag t, tag_map *map, int inc); 11 | tag collapse_tag_map(tag_map map); 12 | void merge_tag_map(tag_map map, tag_map *dst); //slow if many conflicts 13 | int tag_conflict(tag t, tag dst); 14 | int tag_map_conflict(tag t, tag_map map); 15 | int tag_maps_conflict(tag_map m1, tag_map m2); 16 | int tag_map_overconflict(tag t, tag_map map); 17 | int tag_map_overconflicted(tag_map map); 18 | tag_map *zero_tag_map(tag_map *map); 19 | void absorb_tags(group *dst, group *src); 20 | void elevate_tags(group *g, group *parent, group *parent_option_child); 21 | int gtag_required_option(group *g, tag tag_g, tag inv_tag_g, int *ri); 22 | int sprint_group(group *g, int inert, int *utag_dirty, int *gtag_dirty, char *lockholder, char **buff_p, char *devnull); 23 | int tags_coherent_with_selected_option(group *g, tag tag_u, tag tag_g, tag inv_tag_g); 24 | int tags_coherent_with_children(group *g, tag *tag_u, tag *tag_g, tag *inv_tag_g); 25 | void revoke_child_utags(group *g, tag *tag_u); 26 | void aggregate_child_gtags(group *g, tag *tag_g, tag *inv_tag_g); 27 | void merge_group_children_tag_maps(group *g, tag_map *map_u, tag_map *map_g, tag_map *inv_map_g); 28 | int advance_group(group *g, tag current_tag_u, tag current_tag_g, tag current_inv_tag_g); 29 | void zero_progress_group(group *g); 30 | void zero_progress_modifications(group *g); 31 | 32 | #endif //EXPAND_H 33 | -------------------------------------------------------------------------------- /src/sc_expandpass.h: -------------------------------------------------------------------------------- 1 | #include "sc_expandpass.h" 2 | 3 | #include "util.h" 4 | #include "expansion.h" 5 | #include "parse.h" 6 | #include "expand.h" 7 | 8 | group *g; 9 | char *devnull = 0; 10 | 11 | // should this take in a filename? or a file pointer? or a buffer? 12 | void sc_expandpass_init(sc_expandpass_ini ini) 13 | { 14 | FILE *fp = safe_fopen(seed_file, "r"); 15 | if(!fp) { fprintf(stderr,"Error opening seed file: %s\n",seed_file); exit(1); } 16 | g = parse(fp, unquoted); 17 | fclose(fp); 18 | 19 | if(!devnull) devnull = (char *)safe_malloc(sizeof(char)*1024*1024); 20 | 21 | int unroll = 1000; 22 | 23 | collapse_group(g,g,0); 24 | elevate_tags(g,0,0); 25 | prepare_group_iteration(g); 26 | unroll_group(g, unroll, devnull); 27 | collapse_group(g,g,1); 28 | propagate_countability(g); 29 | 30 | if(!g->countable) { fprintf(stderr,"Seed file uncountable: %s\n",seed_file); exit(1); } 31 | } 32 | 33 | int sc_expandpass_keyspace() 34 | { 35 | return estimate_group(g); 36 | } 37 | 38 | void sc_expandpass_seek(int state) 39 | { 40 | resume_countable_group(g,state); 41 | } 42 | 43 | //do I print to stdout? a filepointer? a buffer? 44 | void sc_expandpass_next() 45 | { 46 | char passholder[max_pass_len]; 47 | char *passholder_p = passholder; 48 | char lockholder[max_pass_len]; 49 | memset(lockholder,0,sizeof(char)*max_pass_len); 50 | 51 | int utag_dirty = 0; //guaranteed (tags not allowed w/ hashcat) 52 | int gtag_dirty = 0; //guaranteed (tags not allowed w/ hashcat) 53 | sprint_group(g, 0, &utag_dirty, >ag_dirty, lockholder, &passholder_p, devnull); 54 | } 55 | 56 | void sc_expandpass_shutdown() 57 | { 58 | clean_group_contents(g); 59 | free(g); 60 | } 61 | -------------------------------------------------------------------------------- /example_seeds.txt: -------------------------------------------------------------------------------- 1 | # List of example seeds and their yields. 2 | # Examples are wrapped into a large option group, 3 | # so running this whole file as a seed 4 | # will output all of the examples one after the other. 5 | 6 | # CHEAT SHEET: 7 | # " string (atom) 8 | # < sequence (concatenate) 9 | # { option (choose one) 10 | # ( permutation (shuffle) 11 | # [ modification (modify previous group) 12 | # # comment (ignored) 13 | 14 | # Seeds are normally wrapped with an implicit global <>, 15 | # I have explicitly wrapped each here to put them all in the same file. 16 | 17 | { 18 | 19 | # -------------------------------------- 20 | 21 | < # EXAMPLE 1 (string) 22 | 23 | "banana" 24 | 25 | > # EXAMPLE 1 YIELDS: 26 | # banana 27 | 28 | # -------------------------------------- 29 | 30 | < # EXAMPLE 2 (sequence) 31 | 32 | < 33 | "apple" 34 | "orange" 35 | > 36 | 37 | > # EXAMPLE 2 YIELDS: 38 | # appleorange 39 | 40 | # -------------------------------------- 41 | 42 | < # EXAMPLE 3 (option) 43 | 44 | { 45 | "apple" 46 | "orange" 47 | } 48 | 49 | > # EXAMPLE 3 YIELDS: 50 | # apple 51 | # orange 52 | 53 | # -------------------------------------- 54 | 55 | < # EXAMPLE 4 (option + atom in implicit sequence) 56 | 57 | { 58 | "apple" 59 | "orange" 60 | } 61 | "123" 62 | 63 | > # EXAMPLE 4 YIELDS: 64 | # apple123 65 | # orange123 66 | 67 | # -------------------------------------- 68 | 69 | < # EXAMPLE 5 (two options in implicit sequence) 70 | 71 | { 72 | "apple" 73 | "orange" 74 | } 75 | { 76 | "123" 77 | "abc" 78 | } 79 | 80 | > # EXAMPLE 5 YIELDS: 81 | # apple123 82 | # appleabc 83 | # orange123 84 | # orangeabc 85 | 86 | # -------------------------------------- 87 | 88 | < # EXAMPLE 6 (option with substitution) 89 | 90 | { 91 | "apple" 92 | "orange" 93 | } 94 | [ 95 | s1"abcdefghijklmnopqrstuvwxyz" 96 | ] 97 | 98 | > # EXAMPLE 6 YIELDS: 99 | # apple 100 | # bpple 101 | # cpple 102 | # ... 103 | # applz 104 | # arange 105 | # ... 106 | # orangz 107 | 108 | # -------------------------------------- 109 | 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | int ***cache_permute_indices; 4 | int n_cached_permute_indices = 0; 5 | 6 | void cache_permute_indices_to(int n) 7 | { 8 | if(n_cached_permute_indices <= n) 9 | { 10 | if(!n_cached_permute_indices) cache_permute_indices = (int ***)safe_malloc(sizeof(int **)*(n+1)); 11 | else cache_permute_indices = (int ***)safe_realloc(cache_permute_indices,sizeof(int **)*(n+1)); 12 | for(int i = n_cached_permute_indices; i < n+1; i++) cache_permute_indices[i] = 0; 13 | n_cached_permute_indices = n+1; 14 | } 15 | if(cache_permute_indices[n] == 0) 16 | { 17 | int f = n; 18 | for(int i = n-1; i > 1; i--) f *= i; 19 | cache_permute_indices[n] = (int **)safe_malloc(sizeof(int *)*(f+1)); 20 | cache_permute_indices[n][0] = 0; 21 | cache_permute_indices[n][0] += f; 22 | for(int i = 0; i < f; i++) 23 | cache_permute_indices[n][i+1] = (int *)safe_malloc(sizeof(int)*n); 24 | 25 | //heap's algo- don't really understand 26 | int *c = (int *)safe_malloc(sizeof(int)*n); 27 | for(int i = 0; i < n; i++) c[i] = 0; 28 | for(int i = 0; i < n; i++) cache_permute_indices[n][1][i] = i; 29 | if(f > 1) for(int i = 0; i < n; i++) cache_permute_indices[n][2][i] = i; 30 | int i = 0; 31 | int j = 2; 32 | while(i < n) 33 | { 34 | if(c[i] < i) 35 | { 36 | if(i%2 == 0) { int t = cache_permute_indices[n][j][0 ]; cache_permute_indices[n][j][0 ] = cache_permute_indices[n][j][i]; cache_permute_indices[n][j][i] = t; } 37 | else { int t = cache_permute_indices[n][j][c[i]]; cache_permute_indices[n][j][c[i]] = cache_permute_indices[n][j][i]; cache_permute_indices[n][j][i] = t; } 38 | j++; 39 | if(j < f+1) for(int i = 0; i < n; i++) cache_permute_indices[n][j][i] = cache_permute_indices[n][j-1][i]; 40 | c[i]++; 41 | i = 0; 42 | } 43 | else 44 | { 45 | c[i] = 0; 46 | i++; 47 | } 48 | } 49 | free(c); 50 | 51 | /* 52 | //verify 53 | for(int i = 0; i < f; i++) 54 | { 55 | for(int j = 0; j < n; j++) 56 | { 57 | printf("%d",cache_permute_indices[n][i+1][j]); 58 | } 59 | printf("\n"); 60 | } 61 | */ 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | 2 | - problems: 3 | - - because permutation indexes are pre-cached (for speed+clarity during iteration), permutation groups > 9 become huge memory sinks. > 11 = likely crashes (memory runs out). <- WOULD LOVE HELP! 4 | - - permutations, where an option has potential to be an empty string, should not waste time testing "every possible position of that empty string" 5 | 6 | - features: 7 | - - swizzle modification 8 | - - select (guarantee at least one "select"-tagged option is selected; good for modifying an already exhausted seed without wasting previous attempts) 9 | - - countability (required for seek), at least with core features 10 | - - should warn re: high permutation counts 11 | - - enable "realtime permutation" (which eats cost of inline recursion, but allows for iterative permutation w/o eating up-front memory reqs) 12 | - - or at the very least, estimates w/ recursion 13 | - - unicode 14 | - - validate smartly (know to skip whole groups of modifications/etc... if guaranteed to not succeed) [HARD] 15 | - - parallelize (currently not super necessary- ~1M lines/s) 16 | - - pre-defined modification gamuts (alpha, alphanumeric, etc...) 17 | - - user defined modification gamuts (for easy reuse) 18 | - - user-defined smart substitution gamuts 19 | - - current codebase is pretty loose with (SEED-OPTIMIZATION BASED) memory leaks (as in, rather than recursively delete redundant groups/modifications/etc..., it just leaves cleanup for OS). would be nice to formalize. (however- because it really only is in the seed optimization phase, which relies on very limited memory amounts for a non-recurring amount of time, it's actually not a huge problem) 20 | - - current codebase doesn't have ultra-strict error checking (for example- if you have a password > 500 chars, it might crash) 21 | - - "swap adjacent" modification 22 | - - "insert group" modification (at simplest, to allow for insertion of multichar strings; at most complex, to insert arbitrary strings with their own modifications) 23 | - - estimates which take tags into account 24 | - - hashcat integration: 25 | - - - cleanup garbage rather than quitting and letting OS do it 26 | - - - mark features as "unpredictable", and warn that predictions are perfect/off depending on presence in seed 27 | - - - serialize to/from _single integer_ in case of perfect predictability 28 | - - - wrap direct command line use to use API: 29 | - - - sc_expandpass_init: initialize from struct, including from state variable (int) (error in case of seed using unpredictable features) 30 | - - - sc_expandpass_keyspace: return --estimate if predictable, -1 if not 31 | - - - sc_expandpass_seek: deserialize from int (isn't this already done in init?) 32 | - - - sc_expandpass_next: print next 33 | - - - sc_expandpass_shutdown: cleanup 34 | 35 | - tests needed: 36 | - - more strange whitespace-y seeds 37 | - - tabs-as-whitespace 38 | - - using strange single-character strings with --unquoted 39 | - - take input via stdin 40 | - - check output via -o 41 | - - all possible seed parsing errors 42 | - - input arg errors 43 | - - very complex seeds with --estimate verified against wc -l 44 | - - very complex seeds whose output is piped through grep searching for specific passwords that should be generated 45 | - - pausing/resuming 46 | - - more complex utags/gtags 47 | 48 | - documentation: 49 | - - documentation of seed file in --help or similar 50 | - - documentation of utags and gtags in readme, --help, etc... 51 | 52 | -------------------------------------------------------------------------------- /src/expansion.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPANSION_H 2 | #define EXPANSION_H 3 | 4 | #include "util.h" 5 | 6 | const int version_maj = 0; 7 | const int version_min = 28; 8 | 9 | const int max_pass_len = 300; 10 | const int max_tag_count = 16; 11 | const int max_tag_stack = 4; 12 | 13 | //tag = bitmask representing which tags present 14 | //tag_map = vertical binary count of # of each tag present (eg: "how many [3] tags stamped?" = bitwise concatenation of 3rd bit of each level in the stack) //LOL 15 | //justification for this nonsense: tag is most common representation; bitmask is simple and efficient. "edge case" is aggregation (slow) 16 | 17 | typedef unsigned int tag; 18 | typedef struct 19 | { 20 | tag map[max_tag_stack]; 21 | } tag_map; 22 | 23 | enum GROUP_TYPE 24 | { 25 | GROUP_TYPE_NULL, 26 | GROUP_TYPE_SEQUENCE, 27 | GROUP_TYPE_OPTION, 28 | GROUP_TYPE_PERMUTE, 29 | GROUP_TYPE_CHARS, 30 | GROUP_TYPE_MODIFICATION, //special case, shouldn't exist in formed group 31 | GROUP_TYPE_COUNT, 32 | }; 33 | 34 | struct modification 35 | { 36 | char *chars; 37 | int n; 38 | int n_injections; 39 | int n_smart_substitutions; 40 | int n_substitutions; 41 | int n_deletions; 42 | int n_copys; 43 | int *injection_i; 44 | int *injection_sub_i; 45 | int *substitution_i; 46 | int *substitution_sub_i; 47 | int *smart_substitution_i; 48 | char *smart_substitution_i_c; 49 | int *smart_substitution_sub_i; 50 | int *deletion_i; 51 | }; 52 | inline void zero_modification(modification *m) 53 | { 54 | m->chars = 0; 55 | m->n = 0; 56 | m->n_injections = 0; 57 | m->n_smart_substitutions = 0; 58 | m->n_substitutions = 0; 59 | m->n_deletions = 0; 60 | m->n_copys = 0; 61 | m->injection_i = 0; 62 | m->injection_sub_i = 0; 63 | m->smart_substitution_i = 0; 64 | m->smart_substitution_i_c = 0; 65 | m->smart_substitution_sub_i = 0; 66 | m->substitution_i = 0; 67 | m->substitution_sub_i = 0; 68 | m->deletion_i = 0; 69 | } 70 | 71 | struct group 72 | { 73 | GROUP_TYPE type; 74 | group *childs; 75 | char *chars; 76 | int n; 77 | int i; 78 | modification *mods; 79 | int n_mods; 80 | int mod_i; 81 | tag tag_u; 82 | tag tag_g; 83 | tag child_tag_u; 84 | tag child_tag_g; 85 | tag zerod_sum_tag_u; 86 | tag zerod_sum_tag_g; 87 | tag sum_tag_u; 88 | tag sum_tag_g; 89 | int countable; //TODO: fix countability 90 | int estimate; 91 | }; 92 | inline void zero_group(group *g) 93 | { 94 | g->type = GROUP_TYPE_NULL; 95 | g->childs = 0; 96 | g->chars = 0; 97 | g->n = 0; 98 | g->i = 0; 99 | g->mods = 0; 100 | g->n_mods = 0; 101 | g->mod_i = 0; 102 | g->tag_u = 0; 103 | g->tag_g = 0; 104 | g->child_tag_u = 0; 105 | g->child_tag_g = 0; 106 | g->zerod_sum_tag_u = 0; 107 | g->zerod_sum_tag_g = 0; 108 | g->sum_tag_u = 0; 109 | g->sum_tag_g = 0; 110 | g->countable = 0; 111 | g->estimate = 0; 112 | } 113 | 114 | typedef long long int expand_iter; 115 | 116 | void collapse_group(group *g, group *root, int handle_gamuts); 117 | void prepare_group_iteration(group *g); 118 | void unroll_group(group *g, int threshhold, char *devnull); 119 | void propagate_countability(group *g); 120 | float approximate_length_premodified_group(group *g); 121 | float approximate_length_modified_group(group *g); 122 | expand_iter estimate_group(group *g); 123 | expand_iter state_from_countable_group(group *g); 124 | void resume_countable_group(group *g, expand_iter state); 125 | void free_group_contents(group *g); 126 | void print_seed(group *g, int print_progress, int selected, int indent); 127 | 128 | #endif //EXPANSION_H 129 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifndef _WIN32 10 | #include //for isatty() 11 | #endif //!_WIN32 12 | 13 | const int max_util_read_line_len = 1024*10; 14 | const int max_sprintf_len = 1024; 15 | 16 | //#define BOGUS_SAFETY //"safe" functions just quit on anything funky (allows compilation on VS without complaint) 17 | #ifdef BOGUS_SAFETY 18 | void *safe_malloc(size_t size) 19 | { 20 | void *ptr = malloc(size); 21 | if(!ptr) 22 | { 23 | fprintf(stderr,"malloc failed"); 24 | exit(1); 25 | } 26 | return ptr; 27 | } 28 | void *safe_realloc(void *ptr, size_t size) 29 | { 30 | void *nptr = realloc(ptr,size); 31 | if(!nptr) 32 | { 33 | fprintf(stderr,"realloc failed"); 34 | exit(1); 35 | } 36 | return nptr; 37 | } 38 | char *safe_strcpy(char *dst, char *src) 39 | { 40 | errno_t ret = strcpy_s(dst,max_util_read_line_len,src); //this is extra bogus; does NOT protect against overflow! 41 | if(ret) 42 | { 43 | fprintf(stderr,"strcpy failed"); 44 | exit(1); 45 | } 46 | return dst; 47 | } 48 | int safe_sprintf(char *buff, const char *const fmt, ...) 49 | { 50 | va_list arg; 51 | va_start(arg,fmt); 52 | //char const *vfmt = va_arg(arg, const char *const); 53 | int ret = vsprintf_s(buff, max_sprintf_len, fmt, arg); 54 | if(ret < 0) 55 | { 56 | fprintf(stderr,"sprintf failed"); 57 | exit(1); 58 | } 59 | va_end(arg); 60 | return ret; 61 | } 62 | FILE *safe_fopen(const char *filename, const char *mode) 63 | { 64 | FILE *out; 65 | errno_t ret = fopen_s(&out, filename, mode); 66 | if(ret != 0) 67 | { 68 | return 0; 69 | //fprintf(stderr,"fopen failed"); 70 | //exit(1); 71 | } 72 | return out; 73 | } 74 | int safe_fscanf(FILE *const stream, const char *const fmt, ...) 75 | { 76 | va_list arg; 77 | va_start(arg,fmt); 78 | //char const *vfmt = va_arg(arg, const char *const); 79 | int ret = fscanf_s(stream,fmt,arg); 80 | if(ret == 0 || ret == EOF) 81 | { 82 | fprintf(stderr,"fscanf failed"); 83 | exit(1); 84 | } 85 | va_end(arg); 86 | return ret; 87 | } 88 | #else 89 | #define safe_malloc malloc 90 | #define safe_realloc realloc 91 | #define safe_strcpy strcpy 92 | #define safe_sprintf sprintf 93 | #define safe_fopen fopen 94 | #define safe_fscanf fscanf 95 | #endif 96 | 97 | #ifdef _WIN32 98 | size_t getline(char **lineptr, size_t *n, FILE *stream) //hack recreation of this c11 api 99 | { 100 | const int bsize = max_util_read_line_len-1; 101 | char buffer[bsize+1]; 102 | if(fgets(buffer,*n,stream) == 0) return 0; 103 | for(int i = 0; i < bsize; i++) 104 | { 105 | if(buffer[i] == '\n' || buffer[i] == '\0') 106 | { 107 | if(i == bsize) { fprintf(stderr,"Error reading too large a line on windows (this is not officially supported with windows)\r\n"); exit(1); } 108 | int len = i+1; 109 | buffer[len] = '\0'; 110 | safe_strcpy(*lineptr,buffer); 111 | return len; 112 | } 113 | } 114 | fprintf(stderr,"Error reading too large a line on windows (this is not officially supported with windows)\r\n"); exit(1); 115 | } 116 | #endif 117 | 118 | inline int parse_number(char *b, int *n) 119 | { 120 | int d = 0; 121 | if(!(*b >= '0' && *b <= '9')) return -1; 122 | *n = 0; 123 | while(*b >= '0' && *b <= '9') 124 | { 125 | *n *= 10; 126 | *n += (*b-'0'); 127 | b++; 128 | d++; 129 | } 130 | return d; 131 | } 132 | inline void print_number(int n) 133 | { 134 | int place = 10; 135 | while(place <= n) place *= 10; 136 | place /= 10; 137 | while(place > 0) 138 | { 139 | int d = n/place; 140 | printf("%c",'0'+d); 141 | n -= d*place; 142 | place /= 10; 143 | } 144 | } 145 | 146 | extern int ***cache_permute_indices; 147 | extern int n_cached_permute_indices; 148 | void cache_permute_indices_to(int n); 149 | 150 | inline unsigned long long int ppow(int choose, int option) 151 | { 152 | unsigned long long int t = option; 153 | for(int i = 0; i < choose-1; i++) 154 | t *= option; 155 | return t; 156 | } 157 | 158 | inline unsigned long long int permcomb(int len, int choose, int option) 159 | { 160 | if(choose == 1) return option*len; 161 | if(len == choose) return ppow(choose,option); 162 | 163 | unsigned long long int t = 0; 164 | for(int len_i = len-1; len_i >= (choose-1); len_i--) 165 | t += permcomb(len_i, (choose-1), option)*option; 166 | 167 | return t; 168 | } 169 | 170 | #endif //UTIL_H 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | expandpass is a simple string-expander. Useful for cracking passwords you kinda-remember. 2 | 3 | 4 | # QUICK EXAMPLE: 5 | 6 | Converts a seed file constructed like this: 7 | 8 | ``` 9 | { "apple" "banana" }( "!" "123" ) 10 | ``` 11 | 12 | to a list of expanded strings, like this: 13 | 14 | ``` 15 | apple!123 16 | banana!123 17 | apple123! 18 | banana123! 19 | ``` 20 | 21 | Note: This had to be a (very) short example- because the output grows very fast! 22 | 23 | 24 | # HOW TO USE: 25 | 26 | You define a seed file, and give it to expandpass as an argument (default is `seed.txt`) 27 | 28 | `expandpass -i path/to/seed.txt` 29 | 30 | It outputs the full expansion of that seed to stdout, or you can define an output file with 31 | 32 | `expandpass -o path/to/output.txt` 33 | 34 | It can also be run (and behaves as expected) in standard unix-y way 35 | 36 | `echo '{ "apple" "banana" }[m5]' | expandpass | grep 4` 37 | 38 | 39 | # ARGUMENTS: 40 | 41 | `-i input_file` Specifies file to use as seed (default seed.txt, also accepts stdin w/o specification) 42 | 43 | `-o output_file` Specifies file to print results (default stdout) 44 | 45 | `-b #` Specifies buffer size (bytes) to fill before printing to output (default 1M, experimentally doesn't really alter perf as long as its bigger than ~20 bytes) 46 | 47 | `-f[aA|A|a|#|aA#|@|lmin|lmax] [#]` Filters output by properties (use --help for details). Optional number as argument, quantifying requirement. `expandpass -f# -fa -fA -flmin 10` 48 | 49 | `-c # [progress_file]` Sets how often (default: never) to save progress to a progress file ("set Checkpoint"). Will output # passwords before writing progress to a file (also optionally specified; default: "seed.progress"). `expandpass -c 1000000 my_seed.progress` 50 | 51 | `-r [checkpoint_file]` Resume from optionally defined (default: seed.progress) progress file. Note: a progress file that was created with a different seed file will have unpredictable results. `expandpass -r my_seed.progress` 52 | 53 | `--estimate [@600000]` Prints an estimation of number passwords generated from a given seed file, and prediction of how long it will take to enumerate through them at specified output/s (default: 600000). Note: approximates subgroup lengths in processing modifications; subject to error. `expandpass --estimate @7000` 54 | 55 | `--unroll #` Specifies cutoff of group size, below which is fit for unrolling (optimizing into a single flat options group) (default 1000; 0 == don't unroll). 56 | 57 | `--normalize` Prints normalized/optimized seed file (as used in actual gen). 58 | 59 | `--unquoted` Treats otherwise invalid characters as single-character strings. 60 | 61 | `--help` Shows simple usage menu. `expandpass --help` 62 | 63 | `--version` Shows version. `expandpass --version` 64 | 65 | 66 | # COMPILE: 67 | 68 | expandpass is written using C++, but only uses the c standard library (conversion to pure C should be relatively simple, if necessary). 69 | `g++ gen.cpp -o expandpass` (or similar w/ any C++ compiler) should be sufficient. 70 | There is a Makefile and CMakeLists for the purposes of stubbing out a basic build system, 71 | but they were built with only my simple environment in mind. 72 | 73 | 74 | # THE SEED: 75 | 76 | The "seed" is the nested input to expandpass, describing the desired expansion. 77 | 78 | --------------------- 79 | 80 | **"String"**: The most basic atom of a seed, specified with `""` 81 | 82 | Seed: 83 | ``` 84 | "banana" 85 | ``` 86 | 87 | Output: 88 | ``` 89 | banana 90 | ``` 91 | 92 | Note: The empty string can be specified as `""`, or an unquoted `-` 93 | 94 | Note: To include `"` within a string, escape it with `\"` (example:`"Hello \"World\""` will yield `Hello "World"`). To include `\`, escape it with `\\`. 95 | 96 | --------------------- 97 | 98 | **{Option Group}**: Choose a member, specified by `{}` 99 | 100 | Seed: 101 | ``` 102 | { 103 | "banana" 104 | "apple" 105 | } 106 | ``` 107 | 108 | Output: 109 | ``` 110 | banana 111 | apple 112 | ``` 113 | 114 | --------------------- 115 | 116 | **\**: Concatenate members, specified with `<>` 117 | 118 | Seed: 119 | ``` 120 | < 121 | "banana" 122 | "apple" 123 | > 124 | ``` 125 | 126 | Output: 127 | ``` 128 | bananaapple 129 | ``` 130 | 131 | --------------------- 132 | 133 | **(Permutation Group)**: Shuffle member order and concatenate, specified with `()` 134 | 135 | Seed: 136 | ``` 137 | ( 138 | "banana" 139 | "apple" 140 | ) 141 | ``` 142 | 143 | Output: 144 | ``` 145 | bananaapple 146 | applebanana 147 | ``` 148 | 149 | --------------------- 150 | 151 | **[Modifier]**\*: Modify the *previous* group, specified with `[]` 152 | 153 | \*Modifiers are not "groups" themselves, but rather contain instructions to modify the previous group. (See MODIFIER section for details w/ syntax) 154 | 155 | Seed: 156 | ``` 157 | "banana" 158 | [ s1 "abcdefghijklmnopqrstuvwxyz" ] 159 | ``` 160 | 161 | Output: 162 | ``` 163 | aanana 164 | banana 165 | canana 166 | ... 167 | zanana 168 | banana 169 | bbnana 170 | bcnana 171 | ... 172 | bananz 173 | ``` 174 | 175 | --------------------- 176 | 177 | **Put it together**: 178 | 179 | The real power of the above groupings comes from the fact that all of these things can be arbitrarily nested. For example: 180 | 181 | Seed: 182 | ``` 183 | ( 184 | "Jean" 185 | < { "M" "m" } "arry" { "M" "m" } "e" > 186 | ) 187 | "123" 188 | [-s1 "0123456789"] 189 | { 190 | - 191 | "!" 192 | "!!" 193 | } 194 | ``` 195 | 196 | Output: 197 | 198 | (run it yourself! it's already in `seed.txt`- it should output 744 lines) 199 | 200 | Note: seeds are implicitly are wrapped by a global Sequence Group (`<...>`). 201 | 202 | Note: you can leave comments with an unquoted `#` character. Everything proceeding the `#` on that line will be ignored. 203 | 204 | # MODIFIERS: 205 | 206 | There are 5 types of modifications: **'i'njections**, **'s'ubstitutions**, **'d'eletions**, **s'm'art substitutions**, and **'c'opies**. 207 | 208 | For the sake of simplicity, I'll assume each modification needs to be on its own line (though that isn't syntactually enforced) 209 | 210 | You specify that you want to modify the previously specified group (or string) with `[]`, and you specify what the modification should be within. 211 | For example, if I wanted all instances of "banana" but with one character deleted, I would put in my seed file: 212 | 213 | ``` 214 | "banana" 215 | [ 216 | d1 217 | ] 218 | ``` 219 | 220 | That means, "try all single deletions on this string", yielding: 221 | 222 | ``` 223 | "anana" 224 | "bnana" 225 | "baana" 226 | "banna" 227 | "banaa" 228 | "banan" 229 | ``` 230 | 231 | You can also specify to try all single-character substitutions- but to do so, you must also define a gamut of substitution candidates: 232 | 233 | ``` 234 | "banana" 235 | [ 236 | s1 "ABC" 237 | ] 238 | ``` 239 | 240 | which yields: 241 | 242 | ``` 243 | "Aanana" 244 | "Banana" 245 | "Canana" 246 | "bAnana" 247 | ... 248 | "bananC" 249 | ``` 250 | 251 | Injection is similarly understood: 252 | 253 | ``` 254 | "banana" 255 | [ 256 | i1 "ABC" 257 | ] 258 | ``` 259 | 260 | yeilds: 261 | ``` 262 | "Abanana" 263 | "Bbanana" 264 | "Cbanana" 265 | "bAanana" 266 | ... 267 | "bananaC" 268 | ``` 269 | 270 | Smart Substitution looks at the character and tries common substitutions (no gamut definition required!): 271 | 272 | ``` 273 | "banana" 274 | [ 275 | m1 276 | ] 277 | 278 | "Banana" 279 | "bAnana" 280 | "b4nana" 281 | "baNana" 282 | ... 283 | ``` 284 | 285 | Copy just repeats the previous group n times 286 | 287 | ``` 288 | "banana" 289 | [ 290 | c3 291 | ] 292 | 293 | "bananabananabanana" 294 | ``` 295 | 296 | And again, the power in these comes from their composability. The simplest one is to increment the 1 to a 2 297 | 298 | ``` 299 | "banana" 300 | [ 301 | s2 "ABC" 302 | ] 303 | ``` 304 | 305 | yields: 306 | 307 | ``` 308 | "AAnana" 309 | "ABnana" 310 | ... 311 | "banaCC" 312 | ``` 313 | 314 | Multiple modification types can be composed: 315 | 316 | ``` 317 | "banana" 318 | [ 319 | s1d1 "ABC" 320 | ] 321 | ``` 322 | 323 | ``` 324 | "Anana" 325 | "Bnana" 326 | "Cnana" 327 | "bAana" 328 | "bBana" 329 | "bCana" 330 | ... 331 | ``` 332 | 333 | Note: THIS WILL NOT GUARANTEE TOTAL UNIQUENESS. Deleting the first letter and substituting the second for "A" is identical to deleting the second letter and substituting the first. Combined modifications can thus be redundant. 334 | 335 | You can compose modifications in sequence as well: 336 | 337 | ``` 338 | "banana" 339 | [ 340 | d1 341 | s1 "ABC" 342 | ] 343 | ``` 344 | 345 | yields: 346 | 347 | ``` 348 | "anana" 349 | ... (all the deletes) 350 | "banan" 351 | "Aanana" 352 | "Banana" 353 | .. 354 | ``` 355 | 356 | Note: new modifications in sequence are delineated by either newlines, or a defined gamut. So, the previous seed could alternately be specified as: 357 | 358 | ``` 359 | "banana" [ d1 "" s1 "ABC" ] 360 | ``` 361 | 362 | Finally, the "null" modification is the dash - 363 | 364 | ``` 365 | "banana" 366 | [ 367 | - 368 | d1 369 | ] 370 | ``` 371 | 372 | will ensure `banana` is printed unmodified once before going into the modifications. 373 | 374 | Note: The - is a guaranteed single-modification (doesn't need further delineation). So you could identically specify `"banana"[-d1]` 375 | 376 | 377 | # PERFORMANCE: 378 | 379 | Benchmarked with: 380 | 381 | ``` 382 | time echo '( "Jean" < { "M" "m" } "arry" { "M" "m" } "e" > ) [m5] "123" [-s1 "0123456789"] { "!" "!!" }' | expandpass | wc -l 383 | ``` 384 | 385 | with worst-case output (over a handful of trials): 386 | 387 | ``` 388 | 954800 389 | 390 | real 0m0.507s 391 | user 0m0.500s 392 | sys 0m0.020s 393 | ``` 394 | 395 | on a 2014 Macbook Air, 1.4 GHz Intel Core i5. This comes out to ~2M lines/s. 396 | 397 | Note: There is room for performance improvement. But until I have reason, I have no plan to continue working on this aspect. 398 | That said, some ideas/next steps would be: 399 | 400 | - Cache supgroup output for blit when iteration occurs elsewhere. 401 | - Parallelize (probably lower-hanging fruit than it sounds! generation of each output already relies only on nicely contained state object) 402 | - Edit password iterations in place, rather than complete reconstruction (would require decent refactor. might render more-difficult parallelization). 403 | 404 | 405 | # LICENSE: 406 | 407 | MIT 408 | 409 | 410 | # FURTHER INFO 411 | 412 | See `todo.txt` for things that need doing. 413 | 414 | -------------------------------------------------------------------------------- /tests/out/1: -------------------------------------------------------------------------------- 1 | JeanMarryMe123! 2 | JeanmarryMe123! 3 | JeanMarryme123! 4 | Jeanmarryme123! 5 | MarryMeJean123! 6 | marryMeJean123! 7 | MarrymeJean123! 8 | marrymeJean123! 9 | JeanMarryMe023! 10 | JeanmarryMe023! 11 | JeanMarryme023! 12 | Jeanmarryme023! 13 | MarryMeJean023! 14 | marryMeJean023! 15 | MarrymeJean023! 16 | marrymeJean023! 17 | JeanMarryMe123! 18 | JeanmarryMe123! 19 | JeanMarryme123! 20 | Jeanmarryme123! 21 | MarryMeJean123! 22 | marryMeJean123! 23 | MarrymeJean123! 24 | marrymeJean123! 25 | JeanMarryMe223! 26 | JeanmarryMe223! 27 | JeanMarryme223! 28 | Jeanmarryme223! 29 | MarryMeJean223! 30 | marryMeJean223! 31 | MarrymeJean223! 32 | marrymeJean223! 33 | JeanMarryMe323! 34 | JeanmarryMe323! 35 | JeanMarryme323! 36 | Jeanmarryme323! 37 | MarryMeJean323! 38 | marryMeJean323! 39 | MarrymeJean323! 40 | marrymeJean323! 41 | JeanMarryMe423! 42 | JeanmarryMe423! 43 | JeanMarryme423! 44 | Jeanmarryme423! 45 | MarryMeJean423! 46 | marryMeJean423! 47 | MarrymeJean423! 48 | marrymeJean423! 49 | JeanMarryMe523! 50 | JeanmarryMe523! 51 | JeanMarryme523! 52 | Jeanmarryme523! 53 | MarryMeJean523! 54 | marryMeJean523! 55 | MarrymeJean523! 56 | marrymeJean523! 57 | JeanMarryMe623! 58 | JeanmarryMe623! 59 | JeanMarryme623! 60 | Jeanmarryme623! 61 | MarryMeJean623! 62 | marryMeJean623! 63 | MarrymeJean623! 64 | marrymeJean623! 65 | JeanMarryMe723! 66 | JeanmarryMe723! 67 | JeanMarryme723! 68 | Jeanmarryme723! 69 | MarryMeJean723! 70 | marryMeJean723! 71 | MarrymeJean723! 72 | marrymeJean723! 73 | JeanMarryMe823! 74 | JeanmarryMe823! 75 | JeanMarryme823! 76 | Jeanmarryme823! 77 | MarryMeJean823! 78 | marryMeJean823! 79 | MarrymeJean823! 80 | marrymeJean823! 81 | JeanMarryMe923! 82 | JeanmarryMe923! 83 | JeanMarryme923! 84 | Jeanmarryme923! 85 | MarryMeJean923! 86 | marryMeJean923! 87 | MarrymeJean923! 88 | marrymeJean923! 89 | JeanMarryMe103! 90 | JeanmarryMe103! 91 | JeanMarryme103! 92 | Jeanmarryme103! 93 | MarryMeJean103! 94 | marryMeJean103! 95 | MarrymeJean103! 96 | marrymeJean103! 97 | JeanMarryMe113! 98 | JeanmarryMe113! 99 | JeanMarryme113! 100 | Jeanmarryme113! 101 | MarryMeJean113! 102 | marryMeJean113! 103 | MarrymeJean113! 104 | marrymeJean113! 105 | JeanMarryMe123! 106 | JeanmarryMe123! 107 | JeanMarryme123! 108 | Jeanmarryme123! 109 | MarryMeJean123! 110 | marryMeJean123! 111 | MarrymeJean123! 112 | marrymeJean123! 113 | JeanMarryMe133! 114 | JeanmarryMe133! 115 | JeanMarryme133! 116 | Jeanmarryme133! 117 | MarryMeJean133! 118 | marryMeJean133! 119 | MarrymeJean133! 120 | marrymeJean133! 121 | JeanMarryMe143! 122 | JeanmarryMe143! 123 | JeanMarryme143! 124 | Jeanmarryme143! 125 | MarryMeJean143! 126 | marryMeJean143! 127 | MarrymeJean143! 128 | marrymeJean143! 129 | JeanMarryMe153! 130 | JeanmarryMe153! 131 | JeanMarryme153! 132 | Jeanmarryme153! 133 | MarryMeJean153! 134 | marryMeJean153! 135 | MarrymeJean153! 136 | marrymeJean153! 137 | JeanMarryMe163! 138 | JeanmarryMe163! 139 | JeanMarryme163! 140 | Jeanmarryme163! 141 | MarryMeJean163! 142 | marryMeJean163! 143 | MarrymeJean163! 144 | marrymeJean163! 145 | JeanMarryMe173! 146 | JeanmarryMe173! 147 | JeanMarryme173! 148 | Jeanmarryme173! 149 | MarryMeJean173! 150 | marryMeJean173! 151 | MarrymeJean173! 152 | marrymeJean173! 153 | JeanMarryMe183! 154 | JeanmarryMe183! 155 | JeanMarryme183! 156 | Jeanmarryme183! 157 | MarryMeJean183! 158 | marryMeJean183! 159 | MarrymeJean183! 160 | marrymeJean183! 161 | JeanMarryMe193! 162 | JeanmarryMe193! 163 | JeanMarryme193! 164 | Jeanmarryme193! 165 | MarryMeJean193! 166 | marryMeJean193! 167 | MarrymeJean193! 168 | marrymeJean193! 169 | JeanMarryMe120! 170 | JeanmarryMe120! 171 | JeanMarryme120! 172 | Jeanmarryme120! 173 | MarryMeJean120! 174 | marryMeJean120! 175 | MarrymeJean120! 176 | marrymeJean120! 177 | JeanMarryMe121! 178 | JeanmarryMe121! 179 | JeanMarryme121! 180 | Jeanmarryme121! 181 | MarryMeJean121! 182 | marryMeJean121! 183 | MarrymeJean121! 184 | marrymeJean121! 185 | JeanMarryMe122! 186 | JeanmarryMe122! 187 | JeanMarryme122! 188 | Jeanmarryme122! 189 | MarryMeJean122! 190 | marryMeJean122! 191 | MarrymeJean122! 192 | marrymeJean122! 193 | JeanMarryMe123! 194 | JeanmarryMe123! 195 | JeanMarryme123! 196 | Jeanmarryme123! 197 | MarryMeJean123! 198 | marryMeJean123! 199 | MarrymeJean123! 200 | marrymeJean123! 201 | JeanMarryMe124! 202 | JeanmarryMe124! 203 | JeanMarryme124! 204 | Jeanmarryme124! 205 | MarryMeJean124! 206 | marryMeJean124! 207 | MarrymeJean124! 208 | marrymeJean124! 209 | JeanMarryMe125! 210 | JeanmarryMe125! 211 | JeanMarryme125! 212 | Jeanmarryme125! 213 | MarryMeJean125! 214 | marryMeJean125! 215 | MarrymeJean125! 216 | marrymeJean125! 217 | JeanMarryMe126! 218 | JeanmarryMe126! 219 | JeanMarryme126! 220 | Jeanmarryme126! 221 | MarryMeJean126! 222 | marryMeJean126! 223 | MarrymeJean126! 224 | marrymeJean126! 225 | JeanMarryMe127! 226 | JeanmarryMe127! 227 | JeanMarryme127! 228 | Jeanmarryme127! 229 | MarryMeJean127! 230 | marryMeJean127! 231 | MarrymeJean127! 232 | marrymeJean127! 233 | JeanMarryMe128! 234 | JeanmarryMe128! 235 | JeanMarryme128! 236 | Jeanmarryme128! 237 | MarryMeJean128! 238 | marryMeJean128! 239 | MarrymeJean128! 240 | marrymeJean128! 241 | JeanMarryMe129! 242 | JeanmarryMe129! 243 | JeanMarryme129! 244 | Jeanmarryme129! 245 | MarryMeJean129! 246 | marryMeJean129! 247 | MarrymeJean129! 248 | marrymeJean129! 249 | JeanMarryMe123!! 250 | JeanmarryMe123!! 251 | JeanMarryme123!! 252 | Jeanmarryme123!! 253 | MarryMeJean123!! 254 | marryMeJean123!! 255 | MarrymeJean123!! 256 | marrymeJean123!! 257 | JeanMarryMe023!! 258 | JeanmarryMe023!! 259 | JeanMarryme023!! 260 | Jeanmarryme023!! 261 | MarryMeJean023!! 262 | marryMeJean023!! 263 | MarrymeJean023!! 264 | marrymeJean023!! 265 | JeanMarryMe123!! 266 | JeanmarryMe123!! 267 | JeanMarryme123!! 268 | Jeanmarryme123!! 269 | MarryMeJean123!! 270 | marryMeJean123!! 271 | MarrymeJean123!! 272 | marrymeJean123!! 273 | JeanMarryMe223!! 274 | JeanmarryMe223!! 275 | JeanMarryme223!! 276 | Jeanmarryme223!! 277 | MarryMeJean223!! 278 | marryMeJean223!! 279 | MarrymeJean223!! 280 | marrymeJean223!! 281 | JeanMarryMe323!! 282 | JeanmarryMe323!! 283 | JeanMarryme323!! 284 | Jeanmarryme323!! 285 | MarryMeJean323!! 286 | marryMeJean323!! 287 | MarrymeJean323!! 288 | marrymeJean323!! 289 | JeanMarryMe423!! 290 | JeanmarryMe423!! 291 | JeanMarryme423!! 292 | Jeanmarryme423!! 293 | MarryMeJean423!! 294 | marryMeJean423!! 295 | MarrymeJean423!! 296 | marrymeJean423!! 297 | JeanMarryMe523!! 298 | JeanmarryMe523!! 299 | JeanMarryme523!! 300 | Jeanmarryme523!! 301 | MarryMeJean523!! 302 | marryMeJean523!! 303 | MarrymeJean523!! 304 | marrymeJean523!! 305 | JeanMarryMe623!! 306 | JeanmarryMe623!! 307 | JeanMarryme623!! 308 | Jeanmarryme623!! 309 | MarryMeJean623!! 310 | marryMeJean623!! 311 | MarrymeJean623!! 312 | marrymeJean623!! 313 | JeanMarryMe723!! 314 | JeanmarryMe723!! 315 | JeanMarryme723!! 316 | Jeanmarryme723!! 317 | MarryMeJean723!! 318 | marryMeJean723!! 319 | MarrymeJean723!! 320 | marrymeJean723!! 321 | JeanMarryMe823!! 322 | JeanmarryMe823!! 323 | JeanMarryme823!! 324 | Jeanmarryme823!! 325 | MarryMeJean823!! 326 | marryMeJean823!! 327 | MarrymeJean823!! 328 | marrymeJean823!! 329 | JeanMarryMe923!! 330 | JeanmarryMe923!! 331 | JeanMarryme923!! 332 | Jeanmarryme923!! 333 | MarryMeJean923!! 334 | marryMeJean923!! 335 | MarrymeJean923!! 336 | marrymeJean923!! 337 | JeanMarryMe103!! 338 | JeanmarryMe103!! 339 | JeanMarryme103!! 340 | Jeanmarryme103!! 341 | MarryMeJean103!! 342 | marryMeJean103!! 343 | MarrymeJean103!! 344 | marrymeJean103!! 345 | JeanMarryMe113!! 346 | JeanmarryMe113!! 347 | JeanMarryme113!! 348 | Jeanmarryme113!! 349 | MarryMeJean113!! 350 | marryMeJean113!! 351 | MarrymeJean113!! 352 | marrymeJean113!! 353 | JeanMarryMe123!! 354 | JeanmarryMe123!! 355 | JeanMarryme123!! 356 | Jeanmarryme123!! 357 | MarryMeJean123!! 358 | marryMeJean123!! 359 | MarrymeJean123!! 360 | marrymeJean123!! 361 | JeanMarryMe133!! 362 | JeanmarryMe133!! 363 | JeanMarryme133!! 364 | Jeanmarryme133!! 365 | MarryMeJean133!! 366 | marryMeJean133!! 367 | MarrymeJean133!! 368 | marrymeJean133!! 369 | JeanMarryMe143!! 370 | JeanmarryMe143!! 371 | JeanMarryme143!! 372 | Jeanmarryme143!! 373 | MarryMeJean143!! 374 | marryMeJean143!! 375 | MarrymeJean143!! 376 | marrymeJean143!! 377 | JeanMarryMe153!! 378 | JeanmarryMe153!! 379 | JeanMarryme153!! 380 | Jeanmarryme153!! 381 | MarryMeJean153!! 382 | marryMeJean153!! 383 | MarrymeJean153!! 384 | marrymeJean153!! 385 | JeanMarryMe163!! 386 | JeanmarryMe163!! 387 | JeanMarryme163!! 388 | Jeanmarryme163!! 389 | MarryMeJean163!! 390 | marryMeJean163!! 391 | MarrymeJean163!! 392 | marrymeJean163!! 393 | JeanMarryMe173!! 394 | JeanmarryMe173!! 395 | JeanMarryme173!! 396 | Jeanmarryme173!! 397 | MarryMeJean173!! 398 | marryMeJean173!! 399 | MarrymeJean173!! 400 | marrymeJean173!! 401 | JeanMarryMe183!! 402 | JeanmarryMe183!! 403 | JeanMarryme183!! 404 | Jeanmarryme183!! 405 | MarryMeJean183!! 406 | marryMeJean183!! 407 | MarrymeJean183!! 408 | marrymeJean183!! 409 | JeanMarryMe193!! 410 | JeanmarryMe193!! 411 | JeanMarryme193!! 412 | Jeanmarryme193!! 413 | MarryMeJean193!! 414 | marryMeJean193!! 415 | MarrymeJean193!! 416 | marrymeJean193!! 417 | JeanMarryMe120!! 418 | JeanmarryMe120!! 419 | JeanMarryme120!! 420 | Jeanmarryme120!! 421 | MarryMeJean120!! 422 | marryMeJean120!! 423 | MarrymeJean120!! 424 | marrymeJean120!! 425 | JeanMarryMe121!! 426 | JeanmarryMe121!! 427 | JeanMarryme121!! 428 | Jeanmarryme121!! 429 | MarryMeJean121!! 430 | marryMeJean121!! 431 | MarrymeJean121!! 432 | marrymeJean121!! 433 | JeanMarryMe122!! 434 | JeanmarryMe122!! 435 | JeanMarryme122!! 436 | Jeanmarryme122!! 437 | MarryMeJean122!! 438 | marryMeJean122!! 439 | MarrymeJean122!! 440 | marrymeJean122!! 441 | JeanMarryMe123!! 442 | JeanmarryMe123!! 443 | JeanMarryme123!! 444 | Jeanmarryme123!! 445 | MarryMeJean123!! 446 | marryMeJean123!! 447 | MarrymeJean123!! 448 | marrymeJean123!! 449 | JeanMarryMe124!! 450 | JeanmarryMe124!! 451 | JeanMarryme124!! 452 | Jeanmarryme124!! 453 | MarryMeJean124!! 454 | marryMeJean124!! 455 | MarrymeJean124!! 456 | marrymeJean124!! 457 | JeanMarryMe125!! 458 | JeanmarryMe125!! 459 | JeanMarryme125!! 460 | Jeanmarryme125!! 461 | MarryMeJean125!! 462 | marryMeJean125!! 463 | MarrymeJean125!! 464 | marrymeJean125!! 465 | JeanMarryMe126!! 466 | JeanmarryMe126!! 467 | JeanMarryme126!! 468 | Jeanmarryme126!! 469 | MarryMeJean126!! 470 | marryMeJean126!! 471 | MarrymeJean126!! 472 | marrymeJean126!! 473 | JeanMarryMe127!! 474 | JeanmarryMe127!! 475 | JeanMarryme127!! 476 | Jeanmarryme127!! 477 | MarryMeJean127!! 478 | marryMeJean127!! 479 | MarrymeJean127!! 480 | marrymeJean127!! 481 | JeanMarryMe128!! 482 | JeanmarryMe128!! 483 | JeanMarryme128!! 484 | Jeanmarryme128!! 485 | MarryMeJean128!! 486 | marryMeJean128!! 487 | MarrymeJean128!! 488 | marrymeJean128!! 489 | JeanMarryMe129!! 490 | JeanmarryMe129!! 491 | JeanMarryme129!! 492 | Jeanmarryme129!! 493 | MarryMeJean129!! 494 | marryMeJean129!! 495 | MarrymeJean129!! 496 | marrymeJean129!! 497 | -------------------------------------------------------------------------------- /tests/out/2: -------------------------------------------------------------------------------- 1 | JeanMarryMe123! 2 | JeanmarryMe123! 3 | JeanMarryme123! 4 | Jeanmarryme123! 5 | MarryMeJean123! 6 | marryMeJean123! 7 | MarrymeJean123! 8 | marrymeJean123! 9 | JeanMarryMe023! 10 | JeanmarryMe023! 11 | JeanMarryme023! 12 | Jeanmarryme023! 13 | MarryMeJean023! 14 | marryMeJean023! 15 | MarrymeJean023! 16 | marrymeJean023! 17 | JeanMarryMe123! 18 | JeanmarryMe123! 19 | JeanMarryme123! 20 | Jeanmarryme123! 21 | MarryMeJean123! 22 | marryMeJean123! 23 | MarrymeJean123! 24 | marrymeJean123! 25 | JeanMarryMe223! 26 | JeanmarryMe223! 27 | JeanMarryme223! 28 | Jeanmarryme223! 29 | MarryMeJean223! 30 | marryMeJean223! 31 | MarrymeJean223! 32 | marrymeJean223! 33 | JeanMarryMe323! 34 | JeanmarryMe323! 35 | JeanMarryme323! 36 | Jeanmarryme323! 37 | MarryMeJean323! 38 | marryMeJean323! 39 | MarrymeJean323! 40 | marrymeJean323! 41 | JeanMarryMe423! 42 | JeanmarryMe423! 43 | JeanMarryme423! 44 | Jeanmarryme423! 45 | MarryMeJean423! 46 | marryMeJean423! 47 | MarrymeJean423! 48 | marrymeJean423! 49 | JeanMarryMe523! 50 | JeanmarryMe523! 51 | JeanMarryme523! 52 | Jeanmarryme523! 53 | MarryMeJean523! 54 | marryMeJean523! 55 | MarrymeJean523! 56 | marrymeJean523! 57 | JeanMarryMe623! 58 | JeanmarryMe623! 59 | JeanMarryme623! 60 | Jeanmarryme623! 61 | MarryMeJean623! 62 | marryMeJean623! 63 | MarrymeJean623! 64 | marrymeJean623! 65 | JeanMarryMe723! 66 | JeanmarryMe723! 67 | JeanMarryme723! 68 | Jeanmarryme723! 69 | MarryMeJean723! 70 | marryMeJean723! 71 | MarrymeJean723! 72 | marrymeJean723! 73 | JeanMarryMe823! 74 | JeanmarryMe823! 75 | JeanMarryme823! 76 | Jeanmarryme823! 77 | MarryMeJean823! 78 | marryMeJean823! 79 | MarrymeJean823! 80 | marrymeJean823! 81 | JeanMarryMe923! 82 | JeanmarryMe923! 83 | JeanMarryme923! 84 | Jeanmarryme923! 85 | MarryMeJean923! 86 | marryMeJean923! 87 | MarrymeJean923! 88 | marrymeJean923! 89 | JeanMarryMe103! 90 | JeanmarryMe103! 91 | JeanMarryme103! 92 | Jeanmarryme103! 93 | MarryMeJean103! 94 | marryMeJean103! 95 | MarrymeJean103! 96 | marrymeJean103! 97 | JeanMarryMe113! 98 | JeanmarryMe113! 99 | JeanMarryme113! 100 | Jeanmarryme113! 101 | MarryMeJean113! 102 | marryMeJean113! 103 | MarrymeJean113! 104 | marrymeJean113! 105 | JeanMarryMe123! 106 | JeanmarryMe123! 107 | JeanMarryme123! 108 | Jeanmarryme123! 109 | MarryMeJean123! 110 | marryMeJean123! 111 | MarrymeJean123! 112 | marrymeJean123! 113 | JeanMarryMe133! 114 | JeanmarryMe133! 115 | JeanMarryme133! 116 | Jeanmarryme133! 117 | MarryMeJean133! 118 | marryMeJean133! 119 | MarrymeJean133! 120 | marrymeJean133! 121 | JeanMarryMe143! 122 | JeanmarryMe143! 123 | JeanMarryme143! 124 | Jeanmarryme143! 125 | MarryMeJean143! 126 | marryMeJean143! 127 | MarrymeJean143! 128 | marrymeJean143! 129 | JeanMarryMe153! 130 | JeanmarryMe153! 131 | JeanMarryme153! 132 | Jeanmarryme153! 133 | MarryMeJean153! 134 | marryMeJean153! 135 | MarrymeJean153! 136 | marrymeJean153! 137 | JeanMarryMe163! 138 | JeanmarryMe163! 139 | JeanMarryme163! 140 | Jeanmarryme163! 141 | MarryMeJean163! 142 | marryMeJean163! 143 | MarrymeJean163! 144 | marrymeJean163! 145 | JeanMarryMe173! 146 | JeanmarryMe173! 147 | JeanMarryme173! 148 | Jeanmarryme173! 149 | MarryMeJean173! 150 | marryMeJean173! 151 | MarrymeJean173! 152 | marrymeJean173! 153 | JeanMarryMe183! 154 | JeanmarryMe183! 155 | JeanMarryme183! 156 | Jeanmarryme183! 157 | MarryMeJean183! 158 | marryMeJean183! 159 | MarrymeJean183! 160 | marrymeJean183! 161 | JeanMarryMe193! 162 | JeanmarryMe193! 163 | JeanMarryme193! 164 | Jeanmarryme193! 165 | MarryMeJean193! 166 | marryMeJean193! 167 | MarrymeJean193! 168 | marrymeJean193! 169 | JeanMarryMe120! 170 | JeanmarryMe120! 171 | JeanMarryme120! 172 | Jeanmarryme120! 173 | MarryMeJean120! 174 | marryMeJean120! 175 | MarrymeJean120! 176 | marrymeJean120! 177 | JeanMarryMe121! 178 | JeanmarryMe121! 179 | JeanMarryme121! 180 | Jeanmarryme121! 181 | MarryMeJean121! 182 | marryMeJean121! 183 | MarrymeJean121! 184 | marrymeJean121! 185 | JeanMarryMe122! 186 | JeanmarryMe122! 187 | JeanMarryme122! 188 | Jeanmarryme122! 189 | MarryMeJean122! 190 | marryMeJean122! 191 | MarrymeJean122! 192 | marrymeJean122! 193 | JeanMarryMe123! 194 | JeanmarryMe123! 195 | JeanMarryme123! 196 | Jeanmarryme123! 197 | MarryMeJean123! 198 | marryMeJean123! 199 | MarrymeJean123! 200 | marrymeJean123! 201 | JeanMarryMe124! 202 | JeanmarryMe124! 203 | JeanMarryme124! 204 | Jeanmarryme124! 205 | MarryMeJean124! 206 | marryMeJean124! 207 | MarrymeJean124! 208 | marrymeJean124! 209 | JeanMarryMe125! 210 | JeanmarryMe125! 211 | JeanMarryme125! 212 | Jeanmarryme125! 213 | MarryMeJean125! 214 | marryMeJean125! 215 | MarrymeJean125! 216 | marrymeJean125! 217 | JeanMarryMe126! 218 | JeanmarryMe126! 219 | JeanMarryme126! 220 | Jeanmarryme126! 221 | MarryMeJean126! 222 | marryMeJean126! 223 | MarrymeJean126! 224 | marrymeJean126! 225 | JeanMarryMe127! 226 | JeanmarryMe127! 227 | JeanMarryme127! 228 | Jeanmarryme127! 229 | MarryMeJean127! 230 | marryMeJean127! 231 | MarrymeJean127! 232 | marrymeJean127! 233 | JeanMarryMe128! 234 | JeanmarryMe128! 235 | JeanMarryme128! 236 | Jeanmarryme128! 237 | MarryMeJean128! 238 | marryMeJean128! 239 | MarrymeJean128! 240 | marrymeJean128! 241 | JeanMarryMe129! 242 | JeanmarryMe129! 243 | JeanMarryme129! 244 | Jeanmarryme129! 245 | MarryMeJean129! 246 | marryMeJean129! 247 | MarrymeJean129! 248 | marrymeJean129! 249 | JeanMarryMe123!! 250 | JeanmarryMe123!! 251 | JeanMarryme123!! 252 | Jeanmarryme123!! 253 | MarryMeJean123!! 254 | marryMeJean123!! 255 | MarrymeJean123!! 256 | marrymeJean123!! 257 | JeanMarryMe023!! 258 | JeanmarryMe023!! 259 | JeanMarryme023!! 260 | Jeanmarryme023!! 261 | MarryMeJean023!! 262 | marryMeJean023!! 263 | MarrymeJean023!! 264 | marrymeJean023!! 265 | JeanMarryMe123!! 266 | JeanmarryMe123!! 267 | JeanMarryme123!! 268 | Jeanmarryme123!! 269 | MarryMeJean123!! 270 | marryMeJean123!! 271 | MarrymeJean123!! 272 | marrymeJean123!! 273 | JeanMarryMe223!! 274 | JeanmarryMe223!! 275 | JeanMarryme223!! 276 | Jeanmarryme223!! 277 | MarryMeJean223!! 278 | marryMeJean223!! 279 | MarrymeJean223!! 280 | marrymeJean223!! 281 | JeanMarryMe323!! 282 | JeanmarryMe323!! 283 | JeanMarryme323!! 284 | Jeanmarryme323!! 285 | MarryMeJean323!! 286 | marryMeJean323!! 287 | MarrymeJean323!! 288 | marrymeJean323!! 289 | JeanMarryMe423!! 290 | JeanmarryMe423!! 291 | JeanMarryme423!! 292 | Jeanmarryme423!! 293 | MarryMeJean423!! 294 | marryMeJean423!! 295 | MarrymeJean423!! 296 | marrymeJean423!! 297 | JeanMarryMe523!! 298 | JeanmarryMe523!! 299 | JeanMarryme523!! 300 | Jeanmarryme523!! 301 | MarryMeJean523!! 302 | marryMeJean523!! 303 | MarrymeJean523!! 304 | marrymeJean523!! 305 | JeanMarryMe623!! 306 | JeanmarryMe623!! 307 | JeanMarryme623!! 308 | Jeanmarryme623!! 309 | MarryMeJean623!! 310 | marryMeJean623!! 311 | MarrymeJean623!! 312 | marrymeJean623!! 313 | JeanMarryMe723!! 314 | JeanmarryMe723!! 315 | JeanMarryme723!! 316 | Jeanmarryme723!! 317 | MarryMeJean723!! 318 | marryMeJean723!! 319 | MarrymeJean723!! 320 | marrymeJean723!! 321 | JeanMarryMe823!! 322 | JeanmarryMe823!! 323 | JeanMarryme823!! 324 | Jeanmarryme823!! 325 | MarryMeJean823!! 326 | marryMeJean823!! 327 | MarrymeJean823!! 328 | marrymeJean823!! 329 | JeanMarryMe923!! 330 | JeanmarryMe923!! 331 | JeanMarryme923!! 332 | Jeanmarryme923!! 333 | MarryMeJean923!! 334 | marryMeJean923!! 335 | MarrymeJean923!! 336 | marrymeJean923!! 337 | JeanMarryMe103!! 338 | JeanmarryMe103!! 339 | JeanMarryme103!! 340 | Jeanmarryme103!! 341 | MarryMeJean103!! 342 | marryMeJean103!! 343 | MarrymeJean103!! 344 | marrymeJean103!! 345 | JeanMarryMe113!! 346 | JeanmarryMe113!! 347 | JeanMarryme113!! 348 | Jeanmarryme113!! 349 | MarryMeJean113!! 350 | marryMeJean113!! 351 | MarrymeJean113!! 352 | marrymeJean113!! 353 | JeanMarryMe123!! 354 | JeanmarryMe123!! 355 | JeanMarryme123!! 356 | Jeanmarryme123!! 357 | MarryMeJean123!! 358 | marryMeJean123!! 359 | MarrymeJean123!! 360 | marrymeJean123!! 361 | JeanMarryMe133!! 362 | JeanmarryMe133!! 363 | JeanMarryme133!! 364 | Jeanmarryme133!! 365 | MarryMeJean133!! 366 | marryMeJean133!! 367 | MarrymeJean133!! 368 | marrymeJean133!! 369 | JeanMarryMe143!! 370 | JeanmarryMe143!! 371 | JeanMarryme143!! 372 | Jeanmarryme143!! 373 | MarryMeJean143!! 374 | marryMeJean143!! 375 | MarrymeJean143!! 376 | marrymeJean143!! 377 | JeanMarryMe153!! 378 | JeanmarryMe153!! 379 | JeanMarryme153!! 380 | Jeanmarryme153!! 381 | MarryMeJean153!! 382 | marryMeJean153!! 383 | MarrymeJean153!! 384 | marrymeJean153!! 385 | JeanMarryMe163!! 386 | JeanmarryMe163!! 387 | JeanMarryme163!! 388 | Jeanmarryme163!! 389 | MarryMeJean163!! 390 | marryMeJean163!! 391 | MarrymeJean163!! 392 | marrymeJean163!! 393 | JeanMarryMe173!! 394 | JeanmarryMe173!! 395 | JeanMarryme173!! 396 | Jeanmarryme173!! 397 | MarryMeJean173!! 398 | marryMeJean173!! 399 | MarrymeJean173!! 400 | marrymeJean173!! 401 | JeanMarryMe183!! 402 | JeanmarryMe183!! 403 | JeanMarryme183!! 404 | Jeanmarryme183!! 405 | MarryMeJean183!! 406 | marryMeJean183!! 407 | MarrymeJean183!! 408 | marrymeJean183!! 409 | JeanMarryMe193!! 410 | JeanmarryMe193!! 411 | JeanMarryme193!! 412 | Jeanmarryme193!! 413 | MarryMeJean193!! 414 | marryMeJean193!! 415 | MarrymeJean193!! 416 | marrymeJean193!! 417 | JeanMarryMe120!! 418 | JeanmarryMe120!! 419 | JeanMarryme120!! 420 | Jeanmarryme120!! 421 | MarryMeJean120!! 422 | marryMeJean120!! 423 | MarrymeJean120!! 424 | marrymeJean120!! 425 | JeanMarryMe121!! 426 | JeanmarryMe121!! 427 | JeanMarryme121!! 428 | Jeanmarryme121!! 429 | MarryMeJean121!! 430 | marryMeJean121!! 431 | MarrymeJean121!! 432 | marrymeJean121!! 433 | JeanMarryMe122!! 434 | JeanmarryMe122!! 435 | JeanMarryme122!! 436 | Jeanmarryme122!! 437 | MarryMeJean122!! 438 | marryMeJean122!! 439 | MarrymeJean122!! 440 | marrymeJean122!! 441 | JeanMarryMe123!! 442 | JeanmarryMe123!! 443 | JeanMarryme123!! 444 | Jeanmarryme123!! 445 | MarryMeJean123!! 446 | marryMeJean123!! 447 | MarrymeJean123!! 448 | marrymeJean123!! 449 | JeanMarryMe124!! 450 | JeanmarryMe124!! 451 | JeanMarryme124!! 452 | Jeanmarryme124!! 453 | MarryMeJean124!! 454 | marryMeJean124!! 455 | MarrymeJean124!! 456 | marrymeJean124!! 457 | JeanMarryMe125!! 458 | JeanmarryMe125!! 459 | JeanMarryme125!! 460 | Jeanmarryme125!! 461 | MarryMeJean125!! 462 | marryMeJean125!! 463 | MarrymeJean125!! 464 | marrymeJean125!! 465 | JeanMarryMe126!! 466 | JeanmarryMe126!! 467 | JeanMarryme126!! 468 | Jeanmarryme126!! 469 | MarryMeJean126!! 470 | marryMeJean126!! 471 | MarrymeJean126!! 472 | marrymeJean126!! 473 | JeanMarryMe127!! 474 | JeanmarryMe127!! 475 | JeanMarryme127!! 476 | Jeanmarryme127!! 477 | MarryMeJean127!! 478 | marryMeJean127!! 479 | MarrymeJean127!! 480 | marrymeJean127!! 481 | JeanMarryMe128!! 482 | JeanmarryMe128!! 483 | JeanMarryme128!! 484 | Jeanmarryme128!! 485 | MarryMeJean128!! 486 | marryMeJean128!! 487 | MarrymeJean128!! 488 | marrymeJean128!! 489 | JeanMarryMe129!! 490 | JeanmarryMe129!! 491 | JeanMarryme129!! 492 | Jeanmarryme129!! 493 | MarryMeJean129!! 494 | marryMeJean129!! 495 | MarrymeJean129!! 496 | marrymeJean129!! 497 | -------------------------------------------------------------------------------- /src/expandpass.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "expansion.h" 3 | #include "parse.h" 4 | #include "expand.h" 5 | #include "validate.h" 6 | 7 | void print_estimation(group *g, const char *seed_file, int estimate_rate); 8 | void checkpoint_to_file(const char *filename, group *g); 9 | void checkpoint_group(group *g, FILE *fp); 10 | void resume_from_file(const char *filename, group *g); 11 | void resume_group(group *g, FILE *fp); 12 | 13 | int main(int argc, char **argv) 14 | { 15 | int i = 1; 16 | 17 | const char *seed_file = "seed.txt"; 18 | int seed_specified = 0; 19 | char *password_file = 0; 20 | int resume = 0; 21 | const char *resume_file = "seed.progress"; 22 | int checkpoint = 0; 23 | const char *checkpoint_file = "seed.progress"; 24 | char checkpoint_file_bak[max_sprintf_len]; 25 | checkpoint_file_bak[0] = '\0'; 26 | int normalize = 0; 27 | 28 | int estimate = 0; 29 | int estimate_rate = 600000; 30 | int unroll = 1000; 31 | int unquoted = 0; 32 | 33 | int buff_len = 1024*1024; //1MB 34 | validation v; 35 | zero_validation(&v); 36 | 37 | while(i < argc) 38 | { 39 | if(strcmp(argv[i],"--help") == 0) 40 | { 41 | fprintf(stdout,"usage: expandpass [--help] [--estimate [@#]] [-i input_seed.txt] [-o output_passwords.txt] [--normalize] [--unroll #] [-b #] [-f[aA|A|a|#|aA#|@|l] #] [-c # [checkpoint_seed.progress]] [-r [recovery_seed.progress]]\n"); 42 | fprintf(stdout,"--help shows this menu\n"); 43 | fprintf(stdout,"--version displays version (%d.%d)\n",version_maj,version_min); 44 | fprintf(stdout,"--estimate shows a (crude) estimation of # of likely generatable passwords\n"); 45 | fprintf(stdout,"--normalize prints normalized/optimized seed (as used in actual gen)\n"); 46 | fprintf(stdout,"--unquoted treats otherwise invalid characters as single-character strings\n"); 47 | fprintf(stdout,"--unroll specifies cutoff group size, below which will be optimized (default 1000; 0 == no unrolling)\n"); 48 | fprintf(stdout,"-i specifies seed file (default \"seed.txt\" if blank)\n"); 49 | fprintf(stdout," (see readme for seed syntax)\n"); 50 | fprintf(stdout,"-o specifies output file (default stdout if blank)\n"); 51 | fprintf(stdout,"-b specifies buffer length between output\n"); 52 | fprintf(stdout,"-f filter output, ensuring existence of:\n"); 53 | fprintf(stdout," aA alphabetic character [a-zA-Z]\n"); 54 | fprintf(stdout," A uppercase alphabetic character [A-Z]\n"); 55 | fprintf(stdout," a lowercase alphabetic character [a-z]\n"); 56 | fprintf(stdout," # numeric character [0-9]\n"); 57 | fprintf(stdout," aA# alphanumeric character [a-zA-Z0-9]\n"); 58 | fprintf(stdout," @ non-alphanumeric character [!a-zA-Z0-9]\n"); 59 | fprintf(stdout," optional number specifies required amount in character set (default 1)\n"); 60 | fprintf(stdout,"-flmin filter output ensuring specified minimum length (default 10)\n"); 61 | fprintf(stdout,"-flmax filter output ensuring specified maximum length (default 20)\n"); 62 | fprintf(stdout,"-c specifies how often to checkpoint via progress file (default \"seed.progress\" if blank)\n"); 63 | fprintf(stdout,"-r specifies to resume from progress file (default \"seed.progress\" if blank)\n"); 64 | exit(0); 65 | } 66 | else if(strcmp(argv[i],"--version") == 0) 67 | { 68 | fprintf(stdout,"expandpass version %d.%d\n",version_maj,version_min); 69 | exit(0); 70 | } 71 | else if(strcmp(argv[i],"--estimate") == 0) 72 | { 73 | estimate = 1; 74 | if(i+1 < argc) { i++; if(argv[i][0] == '@') parse_number(argv[i]+1, &estimate_rate); else i--; } 75 | } 76 | else if(strcmp(argv[i],"--unroll") == 0) 77 | { 78 | i++; 79 | if(i >= argc || parse_number(argv[i], &unroll) < 0) 80 | { 81 | fprintf(stderr,"error: no unroll threshhold specified with --unroll\n"); 82 | exit(1); 83 | } 84 | } 85 | else if(strcmp(argv[i],"--normalize") == 0) 86 | { 87 | normalize = 1; 88 | } 89 | else if(strcmp(argv[i],"--unquoted") == 0) 90 | { 91 | unquoted = 1; 92 | } 93 | else if(strcmp(argv[i],"-o") == 0) 94 | { 95 | if(i+1 >= argc) 96 | { 97 | fprintf(stderr,"error: no output file specified with -o\n"); 98 | exit(1); 99 | } 100 | i++; 101 | password_file = argv[i]; 102 | } 103 | else if(strcmp(argv[i],"-i") == 0) 104 | { 105 | if(i+1 >= argc) 106 | { 107 | fprintf(stderr,"error: no input file specified with -i\n"); 108 | exit(1); 109 | } 110 | i++; 111 | seed_file = argv[i]; 112 | seed_specified = 1; 113 | } 114 | else if(strcmp(argv[i],"-b") == 0) 115 | { 116 | i++; 117 | if(i >= argc || parse_number(argv[i], &buff_len) < 0) 118 | { 119 | fprintf(stderr,"error: no buffer size specified with -b\n"); 120 | exit(1); 121 | } 122 | } 123 | else if(strcmp(argv[i],"-faA") == 0) 124 | { 125 | v.alpha = 1; 126 | if(i+1 < argc) 127 | { 128 | i++; 129 | if(parse_number(argv[i], &v.alpha) < 0) i--; 130 | } 131 | } 132 | else if(strcmp(argv[i],"-fA") == 0) 133 | { 134 | v.up_alpha = 1; 135 | if(i+1 < argc) 136 | { 137 | i++; 138 | if(parse_number(argv[i], &v.up_alpha) < 0) i--; 139 | } 140 | } 141 | else if(strcmp(argv[i],"-fa") == 0) 142 | { 143 | v.low_alpha = 1; 144 | if(i+1 < argc) 145 | { 146 | i++; 147 | if(parse_number(argv[i], &v.low_alpha) < 0) i--; 148 | } 149 | } 150 | else if(strcmp(argv[i],"-f#") == 0) 151 | { 152 | v.numeric = 1; 153 | if(i+1 < argc) 154 | { 155 | i++; 156 | if(parse_number(argv[i], &v.numeric) < 0) i--; 157 | } 158 | } 159 | else if(strcmp(argv[i],"-faA#") == 0) 160 | { 161 | v.alphanumeric = 1; 162 | if(i+1 < argc) 163 | { 164 | i++; 165 | if(parse_number(argv[i], &v.alphanumeric) < 0) i--; 166 | } 167 | } 168 | else if(strcmp(argv[i],"-f@") == 0) 169 | { 170 | v.non_alphanumeric = 1; 171 | if(i+1 < argc) 172 | { 173 | i++; 174 | if(parse_number(argv[i], &v.non_alphanumeric) < 0) i--; 175 | } 176 | } 177 | else if(strcmp(argv[i],"-fl") == 0 || strcmp(argv[i],"-flmin") == 0) 178 | { 179 | v.length_min = 10; 180 | if(i+1 < argc) 181 | { 182 | i++; 183 | if(parse_number(argv[i], &v.length_min) < 0) i--; 184 | } 185 | } 186 | else if(strcmp(argv[i],"-flmax") == 0) 187 | { 188 | v.length_max = 20; 189 | if(i+1 < argc) 190 | { 191 | i++; 192 | if(parse_number(argv[i], &v.length_max) < 0) i--; 193 | } 194 | } 195 | else if(strcmp(argv[i],"-c") == 0) 196 | { 197 | checkpoint = 1000000; 198 | if(i+1 < argc) 199 | { 200 | i++; 201 | if(parse_number(argv[i], &checkpoint) < 0) i--; 202 | } 203 | if(i+1 < argc) { i++; checkpoint_file = argv[i]; } 204 | } 205 | else if(strcmp(argv[i],"-r") == 0) 206 | { 207 | resume = 1; 208 | if(i+1 < argc) { i++; resume_file = argv[i]; } 209 | } 210 | else 211 | { 212 | fprintf(stderr,"error: unrecognized argument \"%s\"\n",argv[i]); 213 | exit(1); 214 | } 215 | i++; 216 | } 217 | safe_sprintf(checkpoint_file_bak,"%s.bak",checkpoint_file); 218 | 219 | char *devnull = (char *)safe_malloc(sizeof(char)*buff_len); 220 | 221 | group *g = 0; 222 | { 223 | FILE *fp; 224 | #ifdef _WIN32 225 | int use_stdin = 0; 226 | #else 227 | int use_stdin = !seed_specified && !isatty(fileno(stdin)); 228 | #endif 229 | if(use_stdin) fp = stdin; 230 | else 231 | { 232 | fp = safe_fopen(seed_file, "r"); 233 | if(!fp) { fprintf(stderr,"Error opening seed file: %s\n",seed_file); exit(1); } 234 | } 235 | g = parse(fp, unquoted); 236 | if(!use_stdin) fclose(fp); 237 | } 238 | 239 | collapse_group(g,g,0); 240 | elevate_tags(g,0,0); 241 | prepare_group_iteration(g); 242 | if(unroll) unroll_group(g, unroll, devnull); 243 | collapse_group(g,g,1); 244 | propagate_countability(g); 245 | 246 | if(normalize) 247 | { 248 | print_seed(g,0,0,0); 249 | exit(0); 250 | } 251 | 252 | if(estimate) 253 | { 254 | print_estimation(g,seed_file,estimate_rate); 255 | exit(0); 256 | } 257 | 258 | if(resume) 259 | resume_from_file(resume_file, g); 260 | 261 | FILE *fp; 262 | if(password_file) 263 | { 264 | if(resume) fp = safe_fopen(password_file, "a"); 265 | else fp = safe_fopen(password_file, "w"); 266 | if(!fp) { fprintf(stderr,"Error opening output file: %s\n",password_file); exit(1); } 267 | } 268 | else fp = stdout; 269 | 270 | char *buff = (char *)safe_malloc(sizeof(char)*buff_len); 271 | int buff_i = 0; 272 | char *passholder = (char *)safe_malloc(sizeof(char)*max_pass_len); 273 | char *passholder_p = passholder; 274 | char *lockholder = (char *)safe_malloc(sizeof(char)*max_pass_len); 275 | memset(lockholder,0,sizeof(char)*max_pass_len); 276 | 277 | int done = 0; 278 | expand_iter a = 0; 279 | tag tag_u = 0; 280 | tag tag_g = 0; 281 | tag inv_tag_g = 0; 282 | int utag_dirty = g->zerod_sum_tag_u; 283 | int gtag_dirty = g->zerod_sum_tag_g; 284 | while(!done) 285 | { 286 | //print_seed(g,1,1,0); 287 | if(utag_dirty || gtag_dirty) 288 | { 289 | if(!tags_coherent_with_children(g,clone_tag(0,&tag_u),clone_tag(0,&tag_g),clone_tag(0,&inv_tag_g))) if(advance_group(g,0,0,0)) break; //advance_group returning 1 means done 290 | utag_dirty = 0; 291 | gtag_dirty = 0; 292 | } 293 | passholder_p = passholder; 294 | done = !sprint_group(g, 0, &utag_dirty, >ag_dirty, lockholder, &passholder_p, devnull); 295 | 296 | a++; 297 | *passholder_p = '\0'; 298 | if(validate(&v,passholder,passholder_p-passholder)) 299 | { //append password 300 | int pass_i = 0; 301 | while(passholder[pass_i] != '\0') 302 | { 303 | buff[buff_i] = passholder[pass_i]; 304 | buff_i++; 305 | pass_i++; 306 | if(buff_i >= buff_len) 307 | { 308 | fwrite(buff,sizeof(char),buff_len,fp); 309 | buff_i = 0; 310 | } 311 | } 312 | buff[buff_i] = '\n'; 313 | buff_i++; 314 | if(buff_i >= buff_len) 315 | { 316 | fwrite(buff,sizeof(char),buff_len,fp); 317 | buff_i = 0; 318 | } 319 | } 320 | if(checkpoint && a >= checkpoint) 321 | { 322 | checkpoint_to_file(checkpoint_file_bak,g); 323 | checkpoint_to_file(checkpoint_file ,g); 324 | a = 0; 325 | } 326 | } 327 | 328 | fwrite(buff,sizeof(char),buff_i,fp); 329 | fclose(fp); 330 | } 331 | 332 | void resume_from_file(const char *filename, group *g) 333 | { 334 | FILE *fp; 335 | fp = safe_fopen(filename, "r"); 336 | if(!fp) { fprintf(stderr,"Error opening progress file: %s\n",filename); exit(1); } 337 | resume_group(g,fp); 338 | fclose(fp); 339 | } 340 | 341 | void resume_group(group *g, FILE *fp) 342 | { 343 | safe_fscanf(fp,"%d\n",&g->i); 344 | safe_fscanf(fp,"%d\n",&g->mod_i); 345 | if(g->type != GROUP_TYPE_CHARS) 346 | { 347 | for(int i = 0; i < g->n; i++) 348 | resume_group(&g->childs[i],fp); 349 | } 350 | for(int i = 0; i < g->n_mods; i++) 351 | { 352 | modification *m = &g->mods[i]; 353 | for(int j = 0; j < m->n_injections; j++) 354 | { 355 | safe_fscanf(fp,"%d\n",&m->injection_i[j]); 356 | safe_fscanf(fp,"%d\n",&m->injection_sub_i[j]); 357 | } 358 | for(int j = 0; j < m->n_smart_substitutions; j++) 359 | { 360 | safe_fscanf(fp,"%d\n",&m->smart_substitution_i[j]); 361 | safe_fscanf(fp,"%c\n",&m->smart_substitution_i_c[j]); 362 | safe_fscanf(fp,"%d\n",&m->smart_substitution_sub_i[j]); 363 | } 364 | for(int j = 0; j < m->n_substitutions; j++) 365 | { 366 | safe_fscanf(fp,"%d\n",&m->substitution_i[j]); 367 | safe_fscanf(fp,"%d\n",&m->substitution_sub_i[j]); 368 | } 369 | for(int j = 0; j < m->n_deletions; j++) 370 | { 371 | safe_fscanf(fp,"%d\n",&m->deletion_i[j]); 372 | } 373 | } 374 | } 375 | 376 | void checkpoint_to_file(const char *filename, group *g) 377 | { 378 | FILE *fp; 379 | fp = safe_fopen(filename, "w"); 380 | if(!fp) { fprintf(stderr,"Error opening checkpoint bak file: %s\n",filename); exit(1); } 381 | checkpoint_group(g,fp); 382 | fclose(fp); 383 | } 384 | 385 | void checkpoint_group(group *g, FILE *fp) 386 | { 387 | fprintf(fp,"%d\n",g->i); 388 | fprintf(fp,"%d\n",g->mod_i); 389 | if(g->type != GROUP_TYPE_CHARS) 390 | { 391 | for(int i = 0; i < g->n; i++) 392 | checkpoint_group(&g->childs[i],fp); 393 | } 394 | for(int i = 0; i < g->n_mods; i++) 395 | { 396 | modification *m = &g->mods[i]; 397 | for(int j = 0; j < m->n_injections; j++) 398 | { 399 | fprintf(fp,"%d\n",m->injection_i[j]); 400 | fprintf(fp,"%d\n",m->injection_sub_i[j]); 401 | } 402 | for(int j = 0; j < m->n_smart_substitutions; j++) 403 | { 404 | fprintf(fp,"%d\n",m->smart_substitution_i[j]); 405 | fprintf(fp,"%c\n",m->smart_substitution_i_c[j]); 406 | fprintf(fp,"%d\n",m->smart_substitution_sub_i[j]); 407 | } 408 | for(int j = 0; j < m->n_substitutions; j++) 409 | { 410 | fprintf(fp,"%d\n",m->substitution_i[j]); 411 | fprintf(fp,"%d\n",m->substitution_sub_i[j]); 412 | } 413 | for(int j = 0; j < m->n_deletions; j++) 414 | { 415 | fprintf(fp,"%d\n",m->deletion_i[j]); 416 | } 417 | } 418 | } 419 | 420 | void print_estimation(group *g, const char *seed_file, int estimate_rate) 421 | { 422 | expand_iter a = estimate_group(g); 423 | int d; 424 | int h; 425 | int m; 426 | int s; 427 | fprintf(stdout,"estimated output for seed file (%s): %llu @ %d/s\n",seed_file,a,estimate_rate); 428 | s = a/estimate_rate; 429 | m = s/60; //minute 430 | h = m/60; //hour 431 | d = h/24; //day 432 | if(d) { fprintf(stdout,"%dd ",d); h -= d*24; m -= d*24*60; s -= d*24*60*60; } 433 | if(h) { fprintf(stdout,"%dh ",h); m -= h*60; s -= h*60*60; } 434 | if(m) { fprintf(stdout,"%dm ",m); s -= m*60; } 435 | if(s) { fprintf(stdout,"%ds ",s); ; } 436 | if(d+h+m+s <= 0) { fprintf(stdout,"0s "); } 437 | fprintf(stdout,"\n"); 438 | } 439 | 440 | -------------------------------------------------------------------------------- /src/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "parse.h" 2 | #include "util.h" 3 | 4 | int parse_child(FILE *fp, int unquoted, int *line_n, char *buff, char **b, group *g, group *prev_g, int depth, parse_error *er) 5 | { 6 | g->countable = 1; //until proven otherwise 7 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 8 | size_t line_buff_len = max_read_line_len; 9 | char *s = *b; 10 | while(g->type == GROUP_TYPE_NULL) 11 | { 12 | if(*s == '\0') 13 | { 14 | n_chars = getline(&buff, &line_buff_len, fp); 15 | *line_n = *line_n+1; 16 | if(n_chars <= 0) 17 | { 18 | *buff = '\0'; 19 | er->error = ERROR_EOF; 20 | safe_sprintf(er->txt,"ERROR: EOF\nline %d\n",*line_n); 21 | return 0; 22 | } 23 | s = buff; 24 | } 25 | while(*s == ' ' || *s == '\t') s++; 26 | if(*s == '<' ) { g->type = GROUP_TYPE_SEQUENCE; s++; } 27 | else if(*s == '{' ) { g->type = GROUP_TYPE_OPTION; s++; } 28 | else if(*s == '(' ) { g->type = GROUP_TYPE_PERMUTE; s++; } 29 | else if(*s == '"' ) { g->type = GROUP_TYPE_CHARS; s++; } 30 | else if(*s == '[' ) { g->type = GROUP_TYPE_MODIFICATION; s++; } 31 | else if(*s == '#' ) { g->type = GROUP_TYPE_NULL; s++; *s = '\0'; } 32 | else if(*s == '\n') { g->type = GROUP_TYPE_NULL; s++; *s = '\0'; } 33 | else if(*s == '\0') { g->type = GROUP_TYPE_NULL; s++; *s = '\0'; } 34 | else if(*s == '-') 35 | { 36 | char *c; 37 | g->type = GROUP_TYPE_CHARS; 38 | g->n = 0; 39 | g->chars = (char *)safe_malloc(sizeof(char)*g->n+1); 40 | c = g->chars; 41 | s++; 42 | *c = '\0'; 43 | *b = s; 44 | return 1; 45 | } 46 | else if(unquoted && *s != '>' && *s != '}' && *s != ')' && *s != ']') 47 | { 48 | char *c; 49 | g->type = GROUP_TYPE_CHARS; 50 | if(*s == '\\') s++; 51 | g->n = 1; 52 | g->chars = (char *)safe_malloc(sizeof(char)*g->n+1); 53 | c = g->chars; 54 | *c = *s; 55 | c++; 56 | s++; 57 | *c = '\0'; 58 | *b = s; 59 | return 1; 60 | } 61 | else 62 | { 63 | *b = s; 64 | er->error = ERROR_INVALID_LINE; 65 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 66 | safe_sprintf(er->txt,"ERROR: Invalid line\nline %d ( %s )\n",*line_n,buff); 67 | return 0; 68 | } 69 | 70 | if(g->type != GROUP_TYPE_CHARS) 71 | { 72 | while(*s == ' ' || *s == '\t') s++; 73 | } 74 | 75 | char *c; 76 | if(g->type == GROUP_TYPE_OPTION) g->countable = 0; //TODO: this limitation renders "countability" a farce 77 | switch(g->type) 78 | { 79 | case GROUP_TYPE_SEQUENCE: 80 | case GROUP_TYPE_OPTION: 81 | case GROUP_TYPE_PERMUTE: 82 | g->n = 0; 83 | *b = s; 84 | if(g->type == GROUP_TYPE_SEQUENCE) 85 | { 86 | if(!parse_tag(fp, line_n, buff, b, &g->tag_u, 1, er)) return 0; 87 | if(!parse_tag(fp, line_n, buff, b, &g->tag_g, 0, er)) return 0; 88 | if(g->tag_u) g->countable = 0; 89 | if(g->tag_g) g->countable = 0; 90 | } 91 | return parse_childs(fp, unquoted, line_n, buff, b, g, depth+1, er); 92 | break; 93 | case GROUP_TYPE_CHARS: 94 | c = s; 95 | while(*c != '"' && *c != '\n' && *c != '\0') { if(*c == '\\') c++; c++; } 96 | if(*c == '"') 97 | { 98 | g->n = (c-s); 99 | g->chars = (char *)safe_malloc(sizeof(char)*g->n+1); 100 | c = g->chars; 101 | while(*s != '"' && *s != '\n' && *s != '\0') 102 | { 103 | if(*s == '\\') { s++; g->n--; } 104 | *c = *s; 105 | c++; 106 | s++; 107 | } 108 | if(*s != '"') 109 | { 110 | er->error = ERROR_UNTERMINATED_STRING; 111 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 112 | safe_sprintf(er->txt,"ERROR: Unterminated string\nline %d ( %s )\n",*line_n,buff); 113 | return 0; 114 | } 115 | s++; 116 | *c = '\0'; 117 | } 118 | else 119 | { 120 | er->error = ERROR_INVALID_STRING; 121 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 122 | safe_sprintf(er->txt,"ERROR: Invalid characters after begining string\nline %d ( %s )\n",*line_n,buff); 123 | return 0; 124 | } 125 | break; 126 | case GROUP_TYPE_MODIFICATION: //special case 127 | if(!prev_g) 128 | { 129 | er->error = ERROR_UNPARENTED_MODIFICATION; 130 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 131 | safe_sprintf(er->txt,"ERROR: Modification with no previous group\nline %d ( %s )\n",*line_n,buff); 132 | return 0; 133 | } 134 | *b = s; 135 | return parse_modifications(fp, line_n, buff, b, prev_g, er); 136 | break; 137 | case GROUP_TYPE_NULL: 138 | ; 139 | break; 140 | default: //appease compiler 141 | break; 142 | } 143 | } 144 | *b = s; 145 | return 1; 146 | } 147 | 148 | int parse_tag(FILE *fp, int *line_n, char *buff, char **b, tag *t, int u, parse_error *er) 149 | { 150 | assert(!*t); 151 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 152 | size_t line_buff_len = max_read_line_len; 153 | char *s = *b; 154 | while(1) 155 | { 156 | if(*s == '\0') 157 | { 158 | n_chars = getline(&buff, &line_buff_len, fp); 159 | *line_n = *line_n+1; 160 | if(n_chars <= 0) 161 | { 162 | *buff = '\0'; 163 | er->error = ERROR_BADEOF; 164 | safe_sprintf(er->txt,"ERROR: EOF parsing tag\nline %d\n",*line_n); 165 | return 0; 166 | } 167 | s = buff; 168 | } 169 | else if(*s == ' ' || *s == '\t') s++; 170 | else break; 171 | } 172 | 173 | int parsed = 0; 174 | int d; 175 | if((u && *s == 'U') || (!u && *s == 'G')) 176 | { 177 | s++; 178 | while((d = parse_number(s,&parsed)) != -1) 179 | { 180 | if(parsed < 1 || parsed >= max_tag_count) 181 | { 182 | er->error = ERROR_TAG_RANGE; 183 | if(u) safe_sprintf(er->txt,"ERROR: unique tag outside of valid range [1-%d]\nline %d\n",max_tag_count-1,*line_n); 184 | else safe_sprintf(er->txt,"ERROR: group tag outside of valid range [1-%d]\nline %d\n",max_tag_count-1,*line_n); 185 | return 0; 186 | } 187 | s += d; 188 | *t |= 1 << (parsed-1); 189 | if(*s == ',') s++; 190 | } 191 | if(!*t) 192 | { 193 | er->error = ERROR_TAG_SPECIFY; 194 | if(u) safe_sprintf(er->txt,"ERROR: unique tag not specified\nline %d\n",*line_n); 195 | else safe_sprintf(er->txt,"ERROR: group tag not specified\nline %d\n",*line_n); 196 | return 0; 197 | } 198 | } 199 | *b = s; 200 | return 1; 201 | } 202 | 203 | int parse_modification(FILE *fp, int *line_n, char *buff, char **b, modification *m, parse_error *er) 204 | { 205 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 206 | size_t line_buff_len = max_read_line_len; 207 | char *s = *b; 208 | int found = 0; 209 | while(!found) 210 | { 211 | if(*s == '\0') 212 | { 213 | n_chars = getline(&buff, &line_buff_len, fp); 214 | *line_n = *line_n+1; 215 | if(n_chars <= 0) 216 | { 217 | *buff = '\0'; 218 | er->error = ERROR_BADEOF; 219 | safe_sprintf(er->txt,"ERROR: EOF parsing modification\nline %d\n",*line_n); 220 | return 0; 221 | } 222 | s = buff; 223 | } 224 | 225 | int d = 1; 226 | while(d > 0) 227 | { 228 | while(*s == ' ' || *s == '\t') s++; 229 | if(*s == 'c' ) { s++; d = parse_number(s,&m->n_copys); m->n_copys -= 1; } //m->n_copys = 0 means "just do it once" (ie default) 230 | else if(*s == 'd' ) { s++; d = parse_number(s,&m->n_deletions); } 231 | else if(*s == 's' ) { s++; d = parse_number(s,&m->n_substitutions); } 232 | else if(*s == 'i' ) { s++; d = parse_number(s,&m->n_injections); } 233 | else if(*s == 'm' ) { s++; d = parse_number(s,&m->n_smart_substitutions); } 234 | else if(*s == '-' ) 235 | { 236 | s++; *b = s; 237 | if(m->n_injections+m->n_smart_substitutions+m->n_substitutions+m->n_deletions+m->n_copys == 0) return 1; 238 | else d = -1; 239 | } 240 | else break; 241 | 242 | if(d < 0) 243 | { 244 | er->error = ERROR_INVALID_NULL_MODIFICATION; 245 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 246 | safe_sprintf(er->txt,"ERROR: Invalid null modification\nline %d ( %s )\n",*line_n,buff); 247 | return 0; 248 | } 249 | else 250 | { 251 | s += d; 252 | found = 1; 253 | } 254 | } 255 | 256 | if(!found) 257 | { 258 | if(*s == '#' ) { s++; *s = '\0'; } 259 | else if(*s == '\n') { s++; *s = '\0'; } 260 | else if(*s == '\0') { s++; *s = '\0'; } 261 | else 262 | { 263 | *b = s; 264 | er->error = ERROR_INVALID_MODIFICATION; 265 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 266 | safe_sprintf(er->txt,"ERROR: Invalid line in modification\nline %d ( %s )\n",*line_n,buff); 267 | return 0; 268 | } 269 | } 270 | else 271 | { 272 | if(*s == '#' ) { s++; *s = '\0'; } 273 | else if(*s == '\n') { s++; *s = '\0'; } 274 | else if(*s == '\0') { s++; *s = '\0'; } 275 | else if(*s == '"' ) //get chars 276 | { 277 | s++; 278 | char *c = s; 279 | while(*c != '"' && *c != '\n' && *c != '\0') { if(*c == '\\') c++; c++; } 280 | if(*c == '"') 281 | { 282 | m->n = (c-s); 283 | m->chars = (char *)safe_malloc(sizeof(char)*m->n+1); 284 | c = m->chars; 285 | while(*s != '"' && *s != '\n' && *s != '\0') 286 | { 287 | if(*s == '\\') { s++; m->n--; } 288 | *c = *s; 289 | c++; 290 | s++; 291 | } 292 | if(*s != '"') 293 | { 294 | er->error = ERROR_UNTERMINATED_STRING; 295 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 296 | safe_sprintf(er->txt,"ERROR: Unterminated modification gamut\nline %d ( %s )\n",*line_n,buff); 297 | return 0; 298 | } 299 | s++; 300 | *c = '\0'; 301 | } 302 | else 303 | { 304 | er->error = ERROR_INVALID_STRING; 305 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 306 | safe_sprintf(er->txt,"ERROR: Invalid characters after begining gamut\nline %d ( %s )\n",*line_n,buff); 307 | return 0; 308 | } 309 | } 310 | } 311 | if(m->n_substitutions+m->n_injections > 0 && !m->n) 312 | { 313 | er->error = ERROR_MODIFICATION_EMPTY_GAMUT; 314 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 315 | safe_sprintf(er->txt,"ERROR: gamut required with substitution or injection\nline %d ( %s )\n",*line_n,buff); 316 | return 0; 317 | } 318 | } 319 | *b = s; 320 | return 1; 321 | } 322 | 323 | int parse_modifications(FILE *fp, int *line_n, char *buff, char **b, group *g, parse_error *er) 324 | { 325 | char *s; 326 | int valid_modification = 1; 327 | modification *m = (modification *)safe_malloc(sizeof(modification)); 328 | while(valid_modification) 329 | { 330 | zero_modification(m); 331 | 332 | valid_modification = parse_modification(fp,line_n,buff,b,m,er); 333 | 334 | if(valid_modification) 335 | { 336 | if(m->n_smart_substitutions) g->countable = 0; 337 | g->n_mods++; 338 | if(g->n_mods == 1) g->mods = (modification *)safe_malloc(sizeof(modification)); 339 | else g->mods = (modification *)safe_realloc(g->mods,sizeof(modification)*g->n_mods); 340 | g->mods[g->n_mods-1] = *m; 341 | } 342 | } 343 | free(m); 344 | 345 | if(er->error == ERROR_BADEOF) return 0; //close everything 346 | if(er->error != ERROR_INVALID_MODIFICATION) return 0; //let "invalid modification" attempt parse as "end modification" 347 | if(er->force) return 0; //if error force, don't allow passthrough ("unexpected parse" should only bubble up one level) 348 | 349 | s = *b; 350 | if(*s != ']') 351 | { 352 | //er->error; //retain error from stack 353 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 354 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 355 | safe_sprintf(er->txt,"ERROR: Invalid characters within modifiers\nline %d ( %s )\n",*line_n,buff); 356 | er->force = 1; 357 | return 0; 358 | } 359 | s++; 360 | *b = s; 361 | return 1; 362 | } 363 | 364 | int parse_childs(FILE *fp, int unquoted, int *line_n, char *buff, char **b, group *g, int depth, parse_error *er) 365 | { 366 | char *s; 367 | int valid_kid = 1; 368 | group *prev_c = 0; 369 | group *c = (group *)safe_malloc(sizeof(group)); 370 | while(valid_kid) 371 | { 372 | zero_group(c); 373 | valid_kid = parse_child(fp, unquoted, line_n, buff, b, c, prev_c, depth+1, er); 374 | 375 | if(valid_kid) 376 | { 377 | if(c->type == GROUP_TYPE_MODIFICATION) 378 | { 379 | //ok 380 | } 381 | else if(c->type == GROUP_TYPE_NULL) 382 | { 383 | er->error = ERROR_NULL_CHILD; 384 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 385 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 386 | safe_sprintf(er->txt,"Null child type found\nline %d ( %s )\n",*line_n,buff); 387 | } 388 | else 389 | { 390 | g->n++; 391 | if(g->n == 1) g->childs = (group *)safe_malloc(sizeof(group)); 392 | else g->childs = (group *)safe_realloc(g->childs,sizeof(group)*g->n); 393 | g->childs[g->n-1] = *c; 394 | prev_c = &g->childs[g->n-1]; 395 | } 396 | } 397 | } 398 | free(c); 399 | 400 | if(er->error == ERROR_EOF) 401 | { 402 | if(depth == 0) return 1; //close everything 403 | else 404 | { 405 | er->error = ERROR_BADEOF; //upgrade error 406 | switch(g->type) 407 | { 408 | case GROUP_TYPE_SEQUENCE: safe_sprintf(er->txt,"ERROR: EOF unclosed sequence\nline %d\n",*line_n); break; 409 | case GROUP_TYPE_OPTION: safe_sprintf(er->txt,"ERROR: EOF unclosed option\nline %d\n",*line_n); break; 410 | case GROUP_TYPE_PERMUTE: safe_sprintf(er->txt,"ERROR: EOF unclosed permute\nline %d\n",*line_n); break; 411 | default: break; //appease compiler 412 | } 413 | return 0; 414 | } 415 | } 416 | if(er->error != ERROR_INVALID_LINE) return 0; //let "invalid line" attempt parse as "end line" 417 | if(er->force) return 0; //if error force, don't allow passthrough ("unexpected parse" should only bubble up one level) 418 | 419 | s = *b; 420 | if(*s == '>' && g->type == GROUP_TYPE_SEQUENCE) s++; //great 421 | else if(*s == '}' && g->type == GROUP_TYPE_OPTION) s++; //great 422 | else if(*s == ')' && g->type == GROUP_TYPE_PERMUTE) s++; //great 423 | else 424 | { 425 | //er->error; //retain error from stack 426 | int n_chars = 0; while(buff[n_chars] != '\0') n_chars++; 427 | if(buff[n_chars-1] == '\n') n_chars--; buff[n_chars] = '\0'; 428 | if(g->type == GROUP_TYPE_SEQUENCE) 429 | safe_sprintf(er->txt,"ERROR: Invalid characters within sequence\nline %d ( %s )\n",*line_n,buff); 430 | if(g->type == GROUP_TYPE_OPTION) 431 | safe_sprintf(er->txt,"ERROR: Invalid characters within option\nline %d ( %s )\n",*line_n,buff); 432 | if(g->type == GROUP_TYPE_PERMUTE) 433 | safe_sprintf(er->txt,"ERROR: Invalid characters within permute\nline %d ( %s )\n",*line_n,buff); 434 | er->force = 1; 435 | return 0; 436 | } 437 | 438 | *b = s; 439 | return 1; 440 | } 441 | 442 | group *parse(FILE *fp, int unquoted) 443 | { 444 | char *buff; 445 | 446 | buff = (char *)safe_malloc(sizeof(char)*max_read_line_len); 447 | 448 | parse_error er; 449 | er.error = ERROR_NULL; 450 | er.force = 0; 451 | er.txt = (char *)safe_malloc(sizeof(char)*max_sprintf_len); 452 | int line_n = 0; 453 | 454 | group *g = (group *)safe_malloc(sizeof(group)); 455 | zero_group(g); 456 | g->type = GROUP_TYPE_SEQUENCE; 457 | char *b = buff; 458 | *b = '\0'; 459 | if(!parse_childs(fp, unquoted, &line_n, buff, &b, g, 0, &er)) 460 | { 461 | fprintf(stderr, "%s", er.txt); exit(1); 462 | } 463 | while(g->n == 1 && g->n_mods == 0 && (g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE)) 464 | { 465 | group *og = g->childs; 466 | *g = g->childs[0]; 467 | free(og); 468 | } 469 | 470 | free(buff); 471 | free(er.txt); 472 | 473 | return g; 474 | } 475 | -------------------------------------------------------------------------------- /src/expansion.cpp: -------------------------------------------------------------------------------- 1 | #include "expansion.h" 2 | #include "expand.h" 3 | 4 | void absorb_tags(group *dst, group *src) 5 | { 6 | dst->tag_u |= src->tag_u; 7 | dst->tag_g |= src->tag_g; 8 | src->tag_u = 0; 9 | src->tag_g = 0; 10 | } 11 | 12 | void absorb_gamuts(modification *m, group *g) 13 | { 14 | for(int i = 0; i < g->n_mods; i++) 15 | { 16 | if(g->mods[i].n == m->n && g->mods[i].chars != m->chars) 17 | { 18 | char *a = g->mods[i].chars; 19 | char *b = m->chars; 20 | int j; 21 | for(j = 0; j < m->n && *a == *b; j++) 22 | { 23 | a++; 24 | b++; 25 | } 26 | if(j == m->n) //equal 27 | { 28 | free(g->mods[i].chars); 29 | g->mods[i].chars = m->chars; 30 | } 31 | } 32 | } 33 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE) 34 | { 35 | for(int i = 0; i < g->n; i++) 36 | absorb_gamuts(m, &g->childs[i]); 37 | } 38 | } 39 | 40 | void elevate_tags(group *g, group *parent, group *parent_option_child) 41 | { 42 | if(parent && parent->type == GROUP_TYPE_OPTION) parent_option_child = g; 43 | else if(parent_option_child) absorb_tags(parent_option_child,g); 44 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE) 45 | for(int i = 0; i < g->n; i++) elevate_tags(&g->childs[i],g,parent_option_child); 46 | 47 | g->sum_tag_u = g->tag_u | g->child_tag_u; 48 | g->sum_tag_g = g->tag_g | g->child_tag_g; 49 | g->zerod_sum_tag_u |= g->tag_u; 50 | g->zerod_sum_tag_g |= g->tag_g; 51 | 52 | if(parent) 53 | { 54 | parent->child_tag_u |= (g->tag_u | g->child_tag_u); 55 | parent->child_tag_g |= (g->tag_g | g->child_tag_g); 56 | if(parent->type != GROUP_TYPE_OPTION || g == &parent->childs[0]) 57 | { 58 | parent->zerod_sum_tag_u |= g->zerod_sum_tag_u; 59 | parent->zerod_sum_tag_g |= g->zerod_sum_tag_g; 60 | } 61 | } 62 | } 63 | 64 | float approximate_length_premodified_group(group *g) 65 | { 66 | float l = 0; 67 | switch(g->type) 68 | { 69 | case GROUP_TYPE_SEQUENCE: 70 | l = 0; 71 | for(int i = 0; i < g->n; i++) 72 | l += approximate_length_modified_group(&g->childs[i]); 73 | break; 74 | case GROUP_TYPE_OPTION: 75 | l = 0; 76 | for(int i = 0; i < g->n; i++) 77 | l += approximate_length_modified_group(&g->childs[i]); 78 | l /= g->n; //TODO: taking the average here renders further estimation approximate (ie, uncountable) 79 | break; 80 | case GROUP_TYPE_PERMUTE: 81 | l = 0; 82 | for(int i = 0; i < g->n; i++) 83 | l += approximate_length_modified_group(&g->childs[i]); 84 | break; 85 | case GROUP_TYPE_CHARS: 86 | l = g->n; 87 | break; 88 | default: //appease compile 89 | break; 90 | } 91 | return l; 92 | } 93 | 94 | float approximate_length_modified_group(group *g) 95 | { 96 | float l = approximate_length_premodified_group(g); 97 | if(g->n_mods) 98 | { 99 | float accrue_a = 0; 100 | int a = 0; 101 | for(int i = 0; i < g->n_mods; i++) 102 | { 103 | modification *m = &g->mods[i]; 104 | //if(l > 0 && m->n_smart_substitutions > 0) ; //do nothing 105 | if(l+m->n_injections > 0 && m->n_injections > 0) a += m->n_injections; 106 | //if(l-m->n_smart_substitutions > 0 && m->n_substitutions > 0) ; //do nothing 107 | if(l-m->n_smart_substitutions-m->n_substitutions > 0 && m->n_deletions > 0) a -= m->n_deletions; 108 | accrue_a += a; 109 | a = 0; 110 | } 111 | l += accrue_a/g->n_mods; 112 | } 113 | 114 | return l; 115 | } 116 | 117 | expand_iter estimate_group(group *g) 118 | { 119 | expand_iter a = 1; 120 | switch(g->type) 121 | { 122 | case GROUP_TYPE_SEQUENCE: 123 | a = 1; 124 | for(int i = 0; i < g->n; i++) 125 | a *= estimate_group(&g->childs[i]); 126 | break; 127 | case GROUP_TYPE_OPTION: 128 | a = 0; 129 | for(int i = 0; i < g->n; i++) 130 | a += estimate_group(&g->childs[i]); 131 | break; 132 | case GROUP_TYPE_PERMUTE: 133 | a = 1; 134 | for(int i = 0; i < g->n; i++) 135 | { 136 | a *= estimate_group(&g->childs[i]); 137 | a *= (i+1); 138 | } 139 | break; 140 | case GROUP_TYPE_CHARS: 141 | a = 1; 142 | break; 143 | default: //appease compile 144 | break; 145 | } 146 | if(g->n_mods) 147 | { 148 | int l = approximate_length_premodified_group(g); 149 | expand_iter accrue_a = 0; 150 | expand_iter base_a = a; 151 | a = 1; 152 | for(int i = 0; i < g->n_mods; i++) 153 | { 154 | modification *m = &g->mods[i]; 155 | if(l > 0 && m->n_smart_substitutions > 0) a *= permcomb(l, m->n_smart_substitutions, 2); //assuming 2 is average smart sub 156 | if(l+m->n_injections > 0 && m->n_injections > 0) a *= permcomb(l+m->n_injections, m->n_injections, m->n); 157 | if(l-m->n_smart_substitutions > 0 && m->n_substitutions > 0) a *= permcomb(l-m->n_smart_substitutions, m->n_substitutions, m->n); 158 | if(l-m->n_smart_substitutions-m->n_substitutions > 0 && m->n_deletions > 0) a *= permcomb(l-m->n_smart_substitutions-m->n_substitutions, m->n_deletions, 1); 159 | accrue_a += a*base_a; 160 | a = 1; 161 | } 162 | a = accrue_a; 163 | } 164 | g->estimate = a; 165 | return a; 166 | } 167 | 168 | void collapse_group(group *g, group *root, int handle_gamuts) 169 | { 170 | //remove null modifications 171 | if(g->n_mods == 1 && g->mods[0].n_injections+g->mods[0].n_smart_substitutions+g->mods[0].n_substitutions+g->mods[0].n_deletions+g->mods[0].n_copys == 0) 172 | { 173 | g->n_mods = 0; 174 | free(g->mods); 175 | } 176 | 177 | //collapse single-or-fewer-child, no modification groups 178 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE) 179 | { 180 | for(int i = 0; i < g->n; i++) 181 | { 182 | group *c = &g->childs[i]; 183 | if(c->type == GROUP_TYPE_SEQUENCE || c->type == GROUP_TYPE_OPTION || c->type == GROUP_TYPE_PERMUTE) 184 | { 185 | if(c->n == 1 && c->n_mods == 0 && !c->tag_g) //if c has group tag, then being single-member is potentially meaningful 186 | { 187 | absorb_tags(&c->childs[0],c); //childs[0] will become c 188 | group *oc = c->childs; 189 | *c = c->childs[0]; 190 | free(oc); 191 | i--; 192 | } 193 | else if(c->n < 1) 194 | { 195 | for(int j = i+1; j < g->n; j++) 196 | g->childs[j-1] = g->childs[j]; 197 | //TODO: free memory associated with modifications, etc... (though there really shouldn't be any) 198 | g->n--; 199 | i--; 200 | } 201 | } 202 | } 203 | } 204 | 205 | //collapse like-parent/child options/sequences 206 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION) 207 | { 208 | for(int i = 0; i < g->n; i++) 209 | { 210 | group *c = &g->childs[i]; 211 | if(c->n_mods == 0 && c->type == g->type) 212 | { 213 | if(g->type == GROUP_TYPE_OPTION && c->tag_u) continue; 214 | absorb_tags(g,c); 215 | g->childs = (group *)safe_realloc(g->childs,sizeof(group)*(g->n-1+c->n)); 216 | c = &g->childs[i]; //need to re-assign c, as g has been realloced 217 | group *oc = c->childs; 218 | int ocn = c->n; 219 | for(int j = 0; j < g->n-i; j++) 220 | g->childs[g->n-1+ocn-j-1] = g->childs[g->n-1-j]; 221 | for(int j = 0; j < ocn; j++) 222 | g->childs[i+j] = oc[j]; 223 | free(oc); 224 | g->n += ocn-1; 225 | i--; 226 | } 227 | } 228 | } 229 | 230 | //collapse gamuts 231 | if(handle_gamuts) //gives option to leave gamuts un-absorbed so they can be freed in further optimization 232 | { 233 | modification *m; 234 | for(int i = 0; i < g->n_mods; i++) 235 | { 236 | m = &g->mods[i]; 237 | if(m->n > 0) 238 | absorb_gamuts(m, root); 239 | } 240 | } 241 | 242 | //recurse 243 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE) 244 | { 245 | for(int i = 0; i < g->n; i++) 246 | collapse_group(&g->childs[i], root, handle_gamuts); 247 | } 248 | } 249 | 250 | void prepare_group_iteration(group *g) 251 | { 252 | //alloc/init modifications 253 | modification *m; 254 | for(int i = 0; i < g->n_mods; i++) 255 | { 256 | m = &g->mods[i]; 257 | 258 | if(m->n_injections) { m->injection_i = (int *)safe_malloc(sizeof(int)*m->n_injections); for(int j = 0; j < m->n_injections; j++) m->injection_i[j] = 0; } 259 | if(m->n_smart_substitutions) { m->smart_substitution_i = (int *)safe_malloc(sizeof(int)*m->n_smart_substitutions); for(int j = 0; j < m->n_smart_substitutions; j++) m->smart_substitution_i[j] = j; m->smart_substitution_i_c = (char *)safe_malloc(sizeof(char)*m->n_smart_substitutions); } 260 | if(m->n_substitutions) { m->substitution_i = (int *)safe_malloc(sizeof(int)*m->n_substitutions); for(int j = 0; j < m->n_substitutions; j++) m->substitution_i[j] = j; } 261 | if(m->n_deletions) { m->deletion_i = (int *)safe_malloc(sizeof(int)*m->n_deletions); for(int j = 0; j < m->n_deletions; j++) m->deletion_i[j] = 0; } 262 | 263 | if(m->n_injections) { m->injection_sub_i = (int *)safe_malloc(sizeof(int)*m->n_injections); for(int j = 0; j < m->n_injections; j++) m->injection_sub_i[j] = 0; } 264 | if(m->n_smart_substitutions) { m->smart_substitution_sub_i = (int *)safe_malloc(sizeof(int)*m->n_smart_substitutions); for(int j = 0; j < m->n_smart_substitutions; j++) m->smart_substitution_sub_i[j] = 0; } 265 | if(m->n_substitutions) { m->substitution_sub_i = (int *)safe_malloc(sizeof(int)*m->n_substitutions); for(int j = 0; j < m->n_substitutions; j++) m->substitution_sub_i[j] = 0; } 266 | } 267 | 268 | //ensure permutations cached 269 | if(g->type == GROUP_TYPE_PERMUTE) cache_permute_indices_to(g->n); 270 | 271 | //recurse 272 | if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_OPTION || g->type == GROUP_TYPE_PERMUTE) 273 | for(int i = 0; i < g->n; i++) 274 | prepare_group_iteration(&g->childs[i]); 275 | } 276 | 277 | void unroll_group(group *g, int threshhold, char *devnull) 278 | { 279 | if(g->child_tag_u || g->child_tag_g) return; 280 | int doit = 0; 281 | expand_iter a = estimate_group(g); 282 | if(a < threshhold) 283 | { 284 | if(!doit && g->n_mods) doit = 1; 285 | if(!doit && (g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_PERMUTE)) doit = 1; 286 | if(!doit && g->type == GROUP_TYPE_OPTION) 287 | { 288 | for(int i = 0; !doit && i < g->n; i++) 289 | if(g->childs[i].n_mods || g->childs[i].type != GROUP_TYPE_CHARS) 290 | doit = 1; 291 | } 292 | if(doit)//if !doit, then it's already optimal options or chars 293 | { 294 | char *passholder = (char *)safe_malloc(sizeof(char)*max_pass_len); 295 | char *passholder_p = passholder; 296 | char *lockholder = (char *)safe_malloc(sizeof(char)*max_pass_len); 297 | for(int i = 0; i < max_pass_len; i++) lockholder[i] = 0; 298 | 299 | int done = 0; 300 | group ng; 301 | zero_group(&ng); 302 | ng.type = GROUP_TYPE_OPTION; 303 | absorb_tags(&ng,g); 304 | int utag_dirty = 0; //irrelevant 305 | int gtag_dirty = 0; //irrelevant 306 | group *cg; 307 | int child_malloced = a; 308 | ng.childs = (group *)safe_malloc(sizeof(group)*child_malloced); 309 | while(!done) 310 | { 311 | done = !sprint_group(g, 0, &utag_dirty, >ag_dirty, lockholder, &passholder_p, devnull); 312 | *passholder_p = '\0'; 313 | ng.n++; 314 | if(ng.n > child_malloced) 315 | { 316 | child_malloced *= 2; 317 | ng.childs = (group *)safe_realloc(ng.childs,sizeof(group)*child_malloced); 318 | } 319 | cg = &ng.childs[ng.n-1]; 320 | zero_group(cg); 321 | cg->type = GROUP_TYPE_CHARS; 322 | cg->n = passholder_p-passholder; 323 | cg->chars = (char *)safe_malloc(sizeof(char)*(cg->n+1)); 324 | safe_strcpy(cg->chars,passholder); 325 | passholder_p = passholder; 326 | } 327 | 328 | free(passholder); 329 | free(lockholder); 330 | free_group_contents(g); 331 | *g = ng; 332 | } 333 | } 334 | else if(g->type != GROUP_TYPE_CHARS) 335 | { 336 | for(int i = 0; i < g->n; i++) 337 | unroll_group(&g->childs[i], threshhold, devnull); 338 | } 339 | } 340 | 341 | void propagate_countability(group *g) 342 | { 343 | if(g->type == GROUP_TYPE_CHARS) return; 344 | for(int i = 0; i < g->n; i++) 345 | { 346 | propagate_countability(&g->childs[i]); 347 | if(!g->childs[i].countable) g->countable = 0; 348 | } 349 | } 350 | 351 | expand_iter state_from_countable_group(group *g) 352 | { 353 | expand_iter state = 0; 354 | expand_iter mul = 0; 355 | switch(g->type) 356 | { 357 | case GROUP_TYPE_OPTION: 358 | for(int i = 0; i < g->i; i++) 359 | state += g->childs[i].estimate; 360 | state += state_from_countable_group(&g->childs[g->i]); 361 | break; 362 | case GROUP_TYPE_PERMUTE: 363 | state = 1; 364 | for(int i = 0; i < g->n; i++) 365 | state *= g->childs[i].estimate; 366 | state = g->i*state; 367 | //no break! 368 | case GROUP_TYPE_SEQUENCE: 369 | mul = 1; 370 | for(int i = 0; i < g->n; i++) 371 | mul *= g->childs[i].estimate; 372 | for(int i = 0; i < g->n; i++) 373 | { 374 | mul /= g->childs[i].estimate; 375 | state += mul*state_from_countable_group(&g->childs[g->i]); 376 | } 377 | break; 378 | case GROUP_TYPE_CHARS: 379 | case GROUP_TYPE_MODIFICATION: 380 | default: 381 | state = 1; //not stateful 382 | } 383 | 384 | /* 385 | int l = approximate_length_premodified_group(g); 386 | for(int i = 0; i < g->n_mods; i++) 387 | { 388 | modification *m = &g->mods[i]; 389 | //TODO 390 | } 391 | */ 392 | 393 | return state; 394 | } 395 | 396 | void resume_countable_group(group *g, expand_iter state) 397 | { 398 | //TODO: 399 | } 400 | 401 | void free_modification_contents(modification *m) 402 | { 403 | if(m->chars) free(m->chars); 404 | if(m->injection_i) free(m->injection_i); 405 | if(m->injection_sub_i) free(m->injection_sub_i); 406 | if(m->substitution_i) free(m->substitution_i); 407 | if(m->substitution_sub_i) free(m->substitution_sub_i); 408 | if(m->smart_substitution_i) free(m->smart_substitution_i); 409 | if(m->smart_substitution_i_c) free(m->smart_substitution_i_c); 410 | if(m->smart_substitution_sub_i) free(m->smart_substitution_sub_i); 411 | if(m->deletion_i) free(m->deletion_i); 412 | } 413 | 414 | void free_group_contents(group *g) 415 | { 416 | switch(g->type) 417 | { 418 | case GROUP_TYPE_SEQUENCE: 419 | case GROUP_TYPE_OPTION: 420 | case GROUP_TYPE_PERMUTE: 421 | for(int i = 0; i < g->n; i++) 422 | free_group_contents(&g->childs[i]); 423 | free(g->childs); 424 | break; 425 | case GROUP_TYPE_CHARS: 426 | free(g->chars); 427 | break; 428 | case GROUP_TYPE_NULL: 429 | case GROUP_TYPE_MODIFICATION: 430 | case GROUP_TYPE_COUNT: 431 | default: 432 | //should never hit 433 | break; 434 | } 435 | if(g->n_mods) 436 | { 437 | for(int i = 0; i < g->n_mods; i++) 438 | free_modification_contents(&g->mods[i]); 439 | free(g->mods); 440 | } 441 | } 442 | 443 | void print_tag(tag t, int u) 444 | { 445 | if(u) printf("U"); 446 | else printf("G"); 447 | int first = 1; 448 | for(int i = 0; i < max_tag_count; i++) 449 | { 450 | if(t & (1 << i)) 451 | { 452 | if(!first)printf(",");first = 0; 453 | print_number(i+1); 454 | } 455 | } 456 | } 457 | 458 | void print_seed(group *g, int print_progress, int selected, int indent) 459 | { 460 | switch(g->type) 461 | { 462 | case GROUP_TYPE_SEQUENCE: 463 | for(int i = 0; i < indent; i++) printf(" "); if(selected) printf("*"); printf("<"); if(g->tag_u) print_tag(g->tag_u,1); if(g->tag_g) print_tag(g->tag_g,0); printf("\n"); 464 | for(int i = 0; i < g->n; i++) print_seed(&g->childs[i],print_progress,selected,indent+1); 465 | for(int i = 0; i < indent; i++) printf(" "); printf(">"); if(selected) printf("*"); printf("\n"); 466 | break; 467 | case GROUP_TYPE_OPTION: 468 | for(int i = 0; i < indent; i++) printf(" "); if(selected) printf("*"); if(g->tag_u || g->tag_g) { printf("<"); if(g->tag_u) print_tag(g->tag_u,1); if(g->tag_g) print_tag(g->tag_g,0); } printf("{"); printf("\n"); 469 | for(int i = 0; i < g->n; i++) { print_seed(&g->childs[i],print_progress,print_progress && selected && i == g->i,indent+1); } 470 | for(int i = 0; i < indent; i++) printf(" "); printf("}"); if(g->tag_u || g->tag_g) printf(">"); if(selected) printf("*"); printf("\n"); 471 | break; 472 | case GROUP_TYPE_PERMUTE: 473 | for(int i = 0; i < indent; i++) printf(" "); if(selected) printf("*"); if(g->tag_u || g->tag_g) { printf("<"); if(g->tag_u) print_tag(g->tag_u,1); if(g->tag_g) print_tag(g->tag_g,0); } printf("("); printf("\n"); 474 | if(print_progress) { for(int i = 0; i < g->n; i++) print_seed(&g->childs[cache_permute_indices[g->n][g->i+1][i]],print_progress,selected,indent+1); } 475 | else { for(int i = 0; i < g->n; i++) print_seed(&g->childs[i], print_progress,0,indent+1); } 476 | for(int i = 0; i < indent; i++) printf(" "); printf(")"); if(g->tag_u || g->tag_g) printf(">"); if(selected) printf("*"); printf("\n"); 477 | break; 478 | case GROUP_TYPE_CHARS: 479 | for(int i = 0; i < indent; i++) printf(" "); if(selected) printf("*"); if(g->tag_u || g->tag_g) { printf("<"); if(g->tag_u) print_tag(g->tag_u,1); if(g->tag_g) print_tag(g->tag_g,0); } if(g->n) printf("\"%s\"",g->chars); else printf("-"); if(g->tag_u || g->tag_g) printf(">"); if(selected) printf("*"); printf("\n"); 480 | break; 481 | default: //appease compiler 482 | break; 483 | } 484 | if(g->n_mods) 485 | { 486 | for(int i = 0; i < indent; i++) printf(" "); printf("[\n"); 487 | for(int i = 0; i < g->n_mods; i++) 488 | { 489 | modification *m = &g->mods[i]; 490 | for(int j = 0; j < indent+1; j++) printf(" "); 491 | if(m->n_injections+m->n_smart_substitutions+m->n_substitutions+m->n_deletions+m->n_copys == 0) printf("-\n"); 492 | else 493 | { 494 | if(m->n_injections) printf("i%d ",m->n_injections); 495 | if(m->n_smart_substitutions) printf("m%d ",m->n_smart_substitutions); 496 | if(m->n_substitutions) printf("s%d ",m->n_substitutions); 497 | if(m->n_deletions) printf("d%d ",m->n_deletions); 498 | if(m->n_copys) printf("d%d ",m->n_copys+1); 499 | if(m->n) printf("\"%s\"",m->chars); 500 | printf("\n"); 501 | } 502 | } 503 | for(int i = 0; i < indent; i++) printf(" "); printf("]\n"); 504 | } 505 | } 506 | 507 | -------------------------------------------------------------------------------- /src/expand.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "expand.h" 3 | 4 | char smart_sub(int i, char key) 5 | { 6 | char stub[2] = "a"; 7 | const char *c; 8 | switch(key) 9 | { 10 | case 'a': c = "A4"; break; 11 | case 'b': c = "B"; break; //68 12 | case 'c': c = "C"; break; 13 | case 'd': c = "D"; break; 14 | case 'e': c = "E3"; break; 15 | case 'f': c = "F"; break; 16 | case 'g': c = "G9"; break; 17 | case 'h': c = "H"; break; 18 | case 'i': c = "I!1;"; break; //|lL 19 | case 'j': c = "J"; break; 20 | case 'k': c = "K"; break; 21 | case 'l': c = "L!71"; break; //|iI 22 | case 'm': c = "M"; break; 23 | case 'n': c = "N"; break; 24 | case 'o': c = "O0"; break; 25 | case 'p': c = "P"; break; 26 | case 'q': c = "Q"; break; 27 | case 'r': c = "R"; break; //2 28 | case 's': c = "S5$"; break; //zZ 29 | case 't': c = "T"; break; 30 | case 'u': c = "U"; break; 31 | case 'v': c = "V"; break; 32 | case 'w': c = "W"; break; 33 | case 'x': c = "X"; break; 34 | case 'y': c = "Y"; break; 35 | case 'z': c = "Z2"; break; //sS 36 | case 'A': c = "a4"; break; 37 | case 'B': c = "b"; break; //68 38 | case 'C': c = "c"; break; 39 | case 'D': c = "d"; break; 40 | case 'E': c = "e3"; break; 41 | case 'F': c = "f"; break; 42 | case 'G': c = "g9"; break; 43 | case 'H': c = "h"; break; 44 | case 'I': c = "i!1;"; break; //|lL 45 | case 'J': c = "j"; break; 46 | case 'K': c = "k"; break; 47 | case 'L': c = "l!71"; break; //|iI 48 | case 'M': c = "m"; break; 49 | case 'N': c = "n"; break; 50 | case 'O': c = "o0"; break; 51 | case 'P': c = "p"; break; 52 | case 'Q': c = "q"; break; 53 | case 'R': c = "r"; break; //2 54 | case 'S': c = "s5$"; break; //zZ 55 | case 'T': c = "t"; break; 56 | case 'U': c = "u"; break; 57 | case 'V': c = "v"; break; 58 | case 'W': c = "w"; break; 59 | case 'X': c = "x"; break; 60 | case 'Y': c = "y"; break; 61 | case 'Z': c = "z2"; break; //sS 62 | case '0': c = "oO"; break; 63 | case '1': c = "Ii!"; break; //|lL 64 | case '2': c = "zZ"; break; //R 65 | case '3': c = "eE"; break; 66 | case '4': c = "Aa"; break; 67 | case '5': c = "Ss"; break; 68 | //case '6': c = ""; break; 69 | case '7': c = "Ll"; break; 70 | //case '8': c = ""; break; 71 | case '9': c = "g"; break; 72 | default: stub[0] = key; c = stub; break; 73 | } 74 | return c[i]; 75 | } 76 | 77 | void basic_smart_substitute(int i, int sub_i, char *s) 78 | { 79 | s[i] = smart_sub(sub_i, s[i]); 80 | } 81 | 82 | tag stamp_tag(tag t, tag dst, int inc) 83 | { 84 | if(inc) return dst | t; 85 | else return dst & ~t; 86 | } 87 | 88 | tag *clone_tag(tag src, tag *t) 89 | { 90 | *t = src; 91 | return t; 92 | } 93 | 94 | void stamp_tag_map(tag t, tag_map *map, int inc) 95 | { 96 | if(!t) return; 97 | if(inc) 98 | { 99 | tag carry = 0; 100 | for(int i = 0; t && i < max_tag_stack; i++) 101 | { 102 | carry = t & map->map[i]; 103 | map->map[i] ^= t; 104 | t = carry; 105 | } 106 | if(t) 107 | { 108 | for(int i = 0; t && i < max_tag_stack; i++) 109 | map->map[i] |= t; //don't overflow; max out bits 110 | } 111 | } 112 | else 113 | { 114 | tag carry = 0; 115 | for(int i = 0; t && i < max_tag_stack; i++) 116 | { 117 | carry = t & ~map->map[i]; 118 | map->map[i] ^= t; 119 | t = carry; 120 | } 121 | if(t) 122 | { 123 | for(int i = 0; t && i < max_tag_stack; i++) 124 | map->map[i] &= ~t; //don't underflow; zero out bits 125 | } 126 | } 127 | } 128 | 129 | tag collapse_tag_map(tag_map map) 130 | { 131 | tag t = 0; 132 | for(int i = 0; i < max_tag_stack; i++) 133 | t |= map.map[i]; 134 | return t; 135 | } 136 | 137 | void merge_tag_map(tag_map map, tag_map *dst) //slow if many conflicts 138 | { 139 | if(map.map[0]) stamp_tag_map(map.map[0], dst, 1); 140 | int pow = 2; 141 | for(int i = 1; i < max_tag_stack; i++) 142 | { 143 | if(map.map[i]) for(int j = 0; j < pow; j++) stamp_tag_map(map.map[i], dst, 1); 144 | pow *= 2; 145 | } 146 | } 147 | 148 | int tag_conflict(tag t, tag dst) 149 | { 150 | return t & dst; 151 | } 152 | 153 | int tag_map_conflict(tag t, tag_map map) 154 | { 155 | for(int i = 0; i < max_tag_stack; i++) 156 | if(t & map.map[i]) return 1; 157 | return 0; 158 | } 159 | 160 | int tag_map_overconflict(tag t, tag_map map) 161 | { 162 | for(int i = 1; i < max_tag_stack; i++) 163 | if(t & map.map[i]) return 1; 164 | return 0; 165 | } 166 | 167 | int tag_map_overconflicted(tag_map map) 168 | { 169 | for(int i = 1; i < max_tag_stack; i++) 170 | if(map.map[i]) return 1; 171 | return 0; 172 | } 173 | 174 | tag_map *zero_tag_map(tag_map *map) 175 | { 176 | memset(map,0,sizeof(tag_map)); 177 | return map; 178 | } 179 | 180 | int gtag_required_option(group *g, tag tag_g, tag inv_tag_g, int *ri) 181 | { 182 | if(!tag_g && !inv_tag_g) return -1; 183 | int forced_i = -1; 184 | for(int i = 0; i < g->n; i++) 185 | { 186 | if(tag_g & g->childs[i].tag_g) 187 | { 188 | if(forced_i != -1 || //overcommitted 189 | inv_tag_g & g->childs[i].tag_g) //simultaneously required and prohibited 190 | { 191 | *ri = -1; 192 | return 1; 193 | } 194 | forced_i = i; 195 | } 196 | } 197 | if(forced_i == -1) return 0; 198 | *ri = forced_i; 199 | return 1; 200 | } 201 | 202 | int tick_mod(modification *m) 203 | { 204 | return 0; 205 | } 206 | 207 | int smodify_modification(modification *m, int inert, char *lockholder, char *buff, char **buff_p) 208 | { 209 | char *buff_e = *buff_p; 210 | int flen = buff_e-buff; 211 | for(int i = 0; i < flen; i++) lockholder[i] = 0; 212 | 213 | int open_remaining; 214 | int prev_i; 215 | int fi; 216 | 217 | fi = 0; 218 | for(int i = 0; i < m->n_injections; i++) 219 | { 220 | fi = m->injection_i[i]+i; 221 | char *o = *buff_p; 222 | while(o > (buff+fi)) 223 | { 224 | *o = *(o-1); 225 | o--; 226 | } 227 | *buff_p = *buff_p+1; 228 | *(buff+fi) = m->chars[m->injection_sub_i[i]]; 229 | 230 | lockholder[flen++] = 0; 231 | *(lockholder+fi) = 1; 232 | } 233 | 234 | open_remaining = 0; 235 | prev_i = -1; 236 | fi = 0; 237 | for(int i = 0; i < m->n_smart_substitutions; i++) 238 | { 239 | open_remaining = m->smart_substitution_i[i]-(prev_i+1); 240 | prev_i = m->smart_substitution_i[i]; 241 | while(lockholder[fi]) fi++; 242 | while(open_remaining) 243 | { 244 | fi++; 245 | open_remaining--; 246 | while(lockholder[fi]) fi++; 247 | } 248 | 249 | if(buff+fi >= *buff_p) break; 250 | m->smart_substitution_i_c[i] = buff[fi]; 251 | basic_smart_substitute(fi, m->smart_substitution_sub_i[i], buff); 252 | *(lockholder+fi) = 1; 253 | fi++; 254 | } 255 | 256 | open_remaining = 0; 257 | prev_i = -1; 258 | fi = 0; 259 | for(int i = 0; i < m->n_substitutions; i++) 260 | { 261 | open_remaining = m->substitution_i[i]-(prev_i+1); 262 | prev_i = m->substitution_i[i]; 263 | while(lockholder[fi]) fi++; 264 | while(open_remaining) 265 | { 266 | fi++; 267 | open_remaining--; 268 | while(lockholder[fi]) fi++; 269 | } 270 | 271 | if(buff+fi >= *buff_p) break; 272 | *(buff+fi) = m->chars[m->substitution_sub_i[i]]; 273 | *(lockholder+fi) = 1; 274 | fi++; 275 | } 276 | 277 | open_remaining = 0; 278 | prev_i = 0; 279 | fi = 0; 280 | for(int i = 0; i < m->n_deletions; i++) 281 | { 282 | open_remaining = m->deletion_i[i]-prev_i; 283 | prev_i = m->deletion_i[i]; 284 | while(lockholder[fi]) fi++; 285 | while(open_remaining) 286 | { 287 | fi++; 288 | open_remaining--; 289 | while(lockholder[fi]) fi++; 290 | } 291 | 292 | if(buff+fi >= *buff_p) break; 293 | char *o = (buff+fi); 294 | while(o < *buff_p) 295 | { 296 | *o = *(o+1); 297 | o++; 298 | } 299 | *buff_p = *buff_p-1; 300 | flen--; 301 | for(int j = fi; j < flen; j++) lockholder[j] = lockholder[j+1]; 302 | } 303 | 304 | char *s = buff; 305 | char *o = *buff_p; 306 | for(int i = 0; i < m->n_copys; i++) 307 | { 308 | for(int j = 0; j < flen; j++) 309 | { 310 | *o = *(s+j); 311 | o++; 312 | } 313 | } 314 | *buff_p = o; 315 | 316 | if(!inert) 317 | { 318 | int i; 319 | int flen; 320 | 321 | int n_injections = m->n_injections; 322 | flen = buff_e-buff; 323 | buff_e += n_injections; 324 | i = n_injections; 325 | while(i) 326 | { 327 | i--; 328 | m->injection_sub_i[i]++; 329 | if(m->injection_sub_i[i] == m->n) m->injection_sub_i[i] = 0; 330 | else return 1; 331 | m->injection_i[i]++; 332 | if(m->injection_i[i] > flen) 333 | { 334 | if(i == 0) m->injection_i[i] = 0; 335 | else m->injection_i[i] = m->injection_i[i-1]; 336 | } 337 | else inert = 1; 338 | for(int j = i+1; j < n_injections; j++) 339 | m->injection_i[j] = m->injection_i[j-1]; 340 | if(inert) return inert; 341 | } 342 | 343 | int n_smart_substitutions = m->n_smart_substitutions; 344 | flen = (buff_e-buff)-n_injections; 345 | if(n_smart_substitutions > flen) n_smart_substitutions = flen; 346 | i = n_smart_substitutions; 347 | while(i) 348 | { 349 | i--; 350 | m->smart_substitution_sub_i[i]++; 351 | if(smart_sub(m->smart_substitution_sub_i[i], m->smart_substitution_i_c[i]) == '\0') m->smart_substitution_sub_i[i] = 0; 352 | else return 1; 353 | 354 | m->smart_substitution_i[i]++; 355 | if(m->smart_substitution_i[i] >= flen-(n_smart_substitutions-i-1)) 356 | { 357 | if(i == 0) m->smart_substitution_i[i] = 0; 358 | else m->smart_substitution_i[i] = m->smart_substitution_i[i-1]+1; 359 | } 360 | else inert = 1; 361 | for(int j = i+1; j < n_smart_substitutions; j++) 362 | m->smart_substitution_i[j] = m->smart_substitution_i[j-1]+1; 363 | if(inert) return inert; 364 | } 365 | 366 | int n_substitutions = m->n_substitutions; 367 | flen = (buff_e-buff)-n_injections-n_smart_substitutions; 368 | if(n_substitutions > flen) n_substitutions = flen; 369 | i = n_substitutions; 370 | while(i) 371 | { 372 | i--; 373 | m->substitution_sub_i[i]++; 374 | if(m->substitution_sub_i[i] == m->n) m->substitution_sub_i[i] = 0; 375 | else return 1; 376 | 377 | m->substitution_i[i]++; 378 | if(m->substitution_i[i] >= flen-(n_substitutions-i-1)) 379 | { 380 | if(i == 0) m->substitution_i[i] = 0; 381 | else m->substitution_i[i] = m->substitution_i[i-1]+1; 382 | } 383 | else inert = 1; 384 | for(int j = i+1; j < n_substitutions; j++) 385 | m->substitution_i[j] = m->substitution_i[j-1]+1; 386 | if(inert) return inert; 387 | } 388 | 389 | int n_deletions = m->n_deletions; 390 | flen = (buff_e-buff)-n_injections-n_smart_substitutions-n_substitutions; 391 | if(n_deletions > flen) n_deletions = flen; 392 | i = n_deletions; 393 | buff_e -= i; 394 | while(i) 395 | { 396 | i--; 397 | m->deletion_i[i]++; 398 | if(m->deletion_i[i] >= flen-(n_deletions-1)) 399 | { 400 | if(i == 0) m->deletion_i[i] = 0; 401 | else m->deletion_i[i] = m->deletion_i[i-1]; 402 | } 403 | else inert = 1; 404 | for(int j = i+1; j < n_deletions; j++) 405 | m->deletion_i[j] = m->deletion_i[j-1]; 406 | if(inert) return inert; 407 | } 408 | } 409 | return inert; 410 | } 411 | 412 | int smodify_group(group *g, int inert, char *lockholder, char *buff, char **buff_p) 413 | { 414 | if(!g->n_mods) return inert; 415 | inert = smodify_modification(&g->mods[g->mod_i], inert, lockholder, buff, buff_p); 416 | if(!inert) 417 | { 418 | g->mod_i++; 419 | if(g->mod_i == g->n_mods) g->mod_i = 0; 420 | else inert = 1; 421 | } 422 | return inert; 423 | } 424 | 425 | //prints current state, and manages/advances to _next_ state 426 | int sprint_group(group *g, int inert, int *utag_dirty, int *gtag_dirty, char *lockholder, char **buff_p, char *devnull) //"inert = 1" is the cue to stop incrementing as the rest of the password is written 427 | { 428 | char *tdevnull; 429 | char *buff = *buff_p; 430 | switch(g->type) 431 | { 432 | case GROUP_TYPE_SEQUENCE: 433 | { 434 | if(!inert && g->n_mods) //this is incredibly inefficient 435 | { 436 | for(int i = 0; i < g->n; i++) 437 | sprint_group(&g->childs[i], 1, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); //dry run 438 | inert = smodify_group(g, inert, lockholder, buff, buff_p); //to let modifications get a chance to increment 439 | if(!inert) //still hot? 440 | { 441 | for(int i = 0; i < g->n; i++) 442 | { 443 | tdevnull = devnull; 444 | inert = sprint_group(&g->childs[i], inert, utag_dirty, gtag_dirty, lockholder, &tdevnull, devnull); //redo dry run, updating state (but printing to garbage) 445 | } 446 | } 447 | } 448 | else //inert OR no modifications means no dry run necessary 449 | { 450 | for(int i = 0; i < g->n; i++) 451 | inert = sprint_group(&g->childs[i], inert, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); 452 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 453 | } 454 | } 455 | break; 456 | case GROUP_TYPE_OPTION: 457 | { 458 | if(!inert && g->n_mods) //this is incredibly inefficient 459 | { 460 | sprint_group(&g->childs[g->i], 1, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); 461 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 462 | if(!inert) 463 | { 464 | tdevnull = devnull; 465 | inert = sprint_group(&g->childs[g->i], inert, utag_dirty, gtag_dirty, lockholder, &tdevnull, devnull); 466 | if(!inert) 467 | { 468 | //if(g->childs[g->i].sum_tag_u) *utag_dirty = 1; //"leaving a utag" should never cause an issue 469 | if(g->childs[g->i].sum_tag_g) *gtag_dirty = 1; //more conservative than "necessary" ("should" check against _current state's_ aggregate tag_u; not sum) 470 | g->i++; 471 | if(g->i == g->n) g->i = 0; 472 | else 473 | { 474 | inert = 1; 475 | if(g->childs[g->i].zerod_sum_tag_u) *utag_dirty = 1; 476 | if(g->childs[g->i].sum_tag_g) *gtag_dirty = 1; //more conservative than "necessary" ("should" check if advancing _changed_ the _current state_) 477 | } 478 | } 479 | } 480 | } 481 | else 482 | { 483 | inert = sprint_group(&g->childs[g->i], inert, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); 484 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 485 | if(!inert) 486 | { 487 | //if(g->childs[g->i].sum_tag_u) *utag_dirty = 1; //"leaving a utag" should never cause an issue 488 | if(g->childs[g->i].sum_tag_g) *gtag_dirty = 1; //more conservative than "necessary" ("should" check against _current state's_ aggregate tag_u; not sum) 489 | g->i++; 490 | if(g->i == g->n) g->i = 0; 491 | else 492 | { 493 | inert = 1; 494 | if(g->childs[g->i].zerod_sum_tag_u) *utag_dirty = 1; 495 | if(g->childs[g->i].sum_tag_g) *gtag_dirty = 1; //more conservative than "necessary" ("should" check if advancing _changed_ the _current state_) 496 | } 497 | } 498 | } 499 | } 500 | break; 501 | case GROUP_TYPE_PERMUTE: 502 | { 503 | if(!inert && g->n_mods) //this is incredibly inefficient 504 | { 505 | for(int i = 0; i < g->n; i++) 506 | sprint_group(&g->childs[cache_permute_indices[g->n][g->i+1][i]], 1, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); 507 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 508 | if(!inert) 509 | { 510 | for(int i = 0; i < g->n; i++) 511 | { 512 | tdevnull = devnull; 513 | inert = sprint_group(&g->childs[cache_permute_indices[g->n][g->i+1][i]], inert, utag_dirty, gtag_dirty, lockholder, &tdevnull, devnull); 514 | } 515 | if(!inert) 516 | { 517 | g->i++; 518 | if(g->i == cache_permute_indices[g->n][0]-(int *)0) g->i = 0; 519 | else inert = 1; 520 | } 521 | } 522 | } 523 | else 524 | { 525 | for(int i = 0; i < g->n; i++) 526 | inert = sprint_group(&g->childs[cache_permute_indices[g->n][g->i+1][i]], inert, utag_dirty, gtag_dirty, lockholder, buff_p, devnull); 527 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 528 | if(!inert) 529 | { 530 | g->i++; 531 | if(g->i == cache_permute_indices[g->n][0]-(int *)0) g->i = 0; 532 | else inert = 1; 533 | } 534 | } 535 | } 536 | break; 537 | case GROUP_TYPE_CHARS: 538 | safe_strcpy(buff,g->chars); 539 | *buff_p = buff+g->n; 540 | inert = smodify_group(g, inert, lockholder, buff, buff_p); 541 | break; 542 | default: //appease compiler 543 | break; 544 | } 545 | return inert; 546 | } 547 | 548 | int tags_coherent_with_selected_option(group *g, tag tag_u, tag tag_g, tag inv_tag_g) 549 | { 550 | if(tag_conflict(g->childs[g->i].tag_u,tag_u)) return 0; 551 | if(tag_conflict(g->childs[g->i].tag_g,inv_tag_g)) return 0; 552 | for(int i = 0; i < g->n; i++) 553 | if(i != g->i) if(tag_conflict(g->childs[i].tag_g,tag_g)) return 0; 554 | return 1; 555 | } 556 | 557 | int tags_coherent_with_children(group *g, tag *tag_u, tag *tag_g, tag *inv_tag_g) 558 | { 559 | switch(g->type) 560 | { 561 | case GROUP_TYPE_SEQUENCE: 562 | case GROUP_TYPE_PERMUTE: 563 | for(int i = 0; i < g->n; i++) 564 | if(!tags_coherent_with_children(&g->childs[i], tag_u, tag_g, inv_tag_g)) return 0; 565 | break; 566 | case GROUP_TYPE_OPTION: 567 | { 568 | if(tag_conflict(g->childs[g->i].tag_u,*tag_u)) return 0; 569 | *tag_u = stamp_tag(g->childs[g->i].tag_u,*tag_u,1); 570 | *tag_g = stamp_tag(g->childs[g->i].tag_g,*tag_g,1); 571 | for(int i = 0; i < g->n; i++) 572 | if(i != g->i) *inv_tag_g = stamp_tag(g->childs[i].tag_g,*inv_tag_g,1); 573 | if(tag_conflict(*tag_g,*inv_tag_g)) return 0; 574 | if(!tags_coherent_with_children(&g->childs[g->i], tag_u, tag_g, inv_tag_g)) return 0; 575 | } 576 | break; 577 | case GROUP_TYPE_CHARS: 578 | default: //appease compiler 579 | break; 580 | } 581 | return 1; 582 | } 583 | 584 | void revoke_child_utags(group *g, tag *tag_u) 585 | { 586 | switch(g->type) 587 | { 588 | case GROUP_TYPE_SEQUENCE: 589 | case GROUP_TYPE_PERMUTE: 590 | for(int i = 0; i < g->n; i++) 591 | revoke_child_utags(&g->childs[i],tag_u); 592 | break; 593 | case GROUP_TYPE_OPTION: 594 | *tag_u = stamp_tag(g->childs[g->i].tag_u,*tag_u,0); 595 | revoke_child_utags(&g->childs[g->i],tag_u); 596 | break; 597 | case GROUP_TYPE_CHARS: 598 | default: //appease compiler 599 | break; 600 | } 601 | } 602 | 603 | void aggregate_child_gtags(group *g, tag *tag_g, tag *inv_tag_g) 604 | { 605 | switch(g->type) 606 | { 607 | case GROUP_TYPE_SEQUENCE: 608 | case GROUP_TYPE_PERMUTE: 609 | for(int i = 0; i < g->n; i++) 610 | aggregate_child_gtags(&g->childs[i],tag_g,inv_tag_g); 611 | break; 612 | case GROUP_TYPE_OPTION: 613 | *tag_g = stamp_tag(g->childs[g->i].tag_g,*tag_g,1); 614 | for(int i = 0; i < g->n; i++) 615 | if(i != g->i) *inv_tag_g = stamp_tag(g->childs[i].tag_g,*inv_tag_g,1); 616 | aggregate_child_gtags(&g->childs[g->i],tag_g,inv_tag_g); 617 | break; 618 | case GROUP_TYPE_CHARS: 619 | default: //appease compiler 620 | break; 621 | } 622 | } 623 | 624 | void merge_group_children_tag_maps(group *g, tag_map *map_u, tag_map *map_g, tag_map *inv_map_g) 625 | { 626 | switch(g->type) 627 | { 628 | case GROUP_TYPE_SEQUENCE: 629 | case GROUP_TYPE_PERMUTE: 630 | for(int i = 0; i < g->n; i++) 631 | merge_group_children_tag_maps(&g->childs[i], map_u, map_g, inv_map_g); 632 | break; 633 | case GROUP_TYPE_OPTION: 634 | { 635 | //only need to aggregate here, as the only things with tags should be children of options 636 | stamp_tag_map(g->childs[g->i].tag_u,map_u,1); 637 | stamp_tag_map(g->childs[g->i].tag_g,map_g,1); 638 | for(int i = 0; i < g->n; i++) 639 | if(i != g->i) stamp_tag_map(g->childs[i].tag_g,inv_map_g,1); 640 | merge_group_children_tag_maps(&g->childs[g->i], map_u, map_g, inv_map_g); 641 | } 642 | break; 643 | case GROUP_TYPE_CHARS: 644 | default: //appease compiler 645 | break; 646 | } 647 | } 648 | 649 | int advance_group(group *g, tag current_tag_u, tag current_tag_g, tag current_inv_tag_g) 650 | { 651 | int carry = 0; 652 | tag og_tag_g = current_tag_g; 653 | tag og_inv_tag_g = current_inv_tag_g; 654 | tag proposed_tag_u; 655 | tag proposed_tag_g; 656 | tag proposed_inv_tag_g; 657 | switch(g->type) 658 | { 659 | case GROUP_TYPE_SEQUENCE: 660 | { 661 | int i = g->n-1; 662 | int advanced = 0; 663 | while(i >= 0) 664 | { 665 | int newly_advanced = 0; 666 | if(carry || (i == 0 && !advanced)) 667 | { 668 | advanced = 1; 669 | newly_advanced = 1; 670 | carry = advance_group(&g->childs[i], current_tag_u, current_tag_g, current_inv_tag_g); 671 | } 672 | while(!carry && !tags_coherent_with_children(&g->childs[i],clone_tag(current_tag_u,&proposed_tag_u),clone_tag(current_tag_g,&proposed_tag_g),clone_tag(current_inv_tag_g,&proposed_inv_tag_g))) //to get out of this loop, we're either carrying or succeeding in populating proposed_* 673 | { 674 | advanced = 1; 675 | newly_advanced = 1; 676 | carry = advance_group(&g->childs[i], current_tag_u, current_tag_g, current_inv_tag_g); //assume success || carry 677 | } 678 | if(newly_advanced) 679 | { 680 | for(int j = i-1; j >= 0; j--) 681 | zero_progress_group(&g->childs[j]); 682 | } 683 | if(carry) 684 | { 685 | i++; 686 | if(i == g->n) //tried to pop too far: impossible group 687 | { 688 | zero_progress_group(g); 689 | return 1; //"carry" 690 | } 691 | else 692 | { 693 | //going to advance this next child on the carry; remove its influence 694 | revoke_child_utags(&g->childs[i],¤t_tag_u); //because utags don't stack, it's easy to undo 695 | 696 | //rather than managing stack of gtags, just flush and re-aggregate 697 | clone_tag(og_tag_g,¤t_tag_g); 698 | clone_tag(og_inv_tag_g,¤t_inv_tag_g); 699 | for(int j = g->n-1; j > i; j--) 700 | aggregate_child_gtags(&g->childs[j],¤t_tag_g,¤t_inv_tag_g); 701 | } 702 | } 703 | else 704 | { 705 | current_tag_u = proposed_tag_u; 706 | current_tag_g = proposed_tag_g; 707 | current_inv_tag_g = proposed_inv_tag_g; 708 | i--; 709 | } 710 | } 711 | } 712 | break; 713 | case GROUP_TYPE_OPTION: 714 | { 715 | int advanced = 0; 716 | carry = 0; 717 | while(!advanced || carry) 718 | { 719 | while(carry || !tags_coherent_with_selected_option(g, current_tag_u, current_tag_g, current_inv_tag_g)) 720 | { 721 | int i = 0; 722 | int forced = 0; 723 | if(current_tag_g) forced = gtag_required_option(g,current_tag_g,current_inv_tag_g,&i); 724 | if(!forced) i = g->i+1; 725 | if(i == -1 || i <= g->i || i == g->n) //over committed || can't advance (already correctly locked) || can't advance further 726 | { 727 | zero_progress_group(g); 728 | return 1; 729 | } 730 | 731 | g->i = i; 732 | advanced = 1; 733 | carry = 0; 734 | } 735 | 736 | if(!advanced) carry = advance_group(&g->childs[g->i], current_tag_u, current_tag_g, current_inv_tag_g); 737 | advanced = 1; 738 | while(!carry && !tags_coherent_with_children(&g->childs[g->i],clone_tag(current_tag_u,&proposed_tag_u),clone_tag(current_tag_g,&proposed_tag_g),clone_tag(current_inv_tag_g,&proposed_inv_tag_g))) //to get out of this loop, we're either carrying or succeeding in populating proposed_* 739 | { 740 | carry = advance_group(&g->childs[g->i], current_tag_u, current_tag_g, current_inv_tag_g); 741 | } 742 | if(carry) 743 | { 744 | current_tag_u = stamp_tag(g->childs[g->i].tag_u,current_tag_u,0); 745 | current_tag_g = og_tag_g; 746 | current_inv_tag_g = og_inv_tag_g; 747 | } 748 | } 749 | } 750 | break; 751 | case GROUP_TYPE_PERMUTE: 752 | { 753 | int i = g->n-1; 754 | int advanced = 0; 755 | while(i >= 0) 756 | { 757 | int pi = cache_permute_indices[g->n][g->i+1][i]; 758 | int newly_advanced = 0; 759 | if(carry || (i == 0 && !advanced)) 760 | { 761 | advanced = 1; 762 | newly_advanced = 1; 763 | carry = advance_group(&g->childs[pi], current_tag_u, current_tag_g, current_inv_tag_g); 764 | } 765 | while(!carry && !tags_coherent_with_children(&g->childs[i],clone_tag(current_tag_u,&proposed_tag_u),clone_tag(current_tag_g,&proposed_tag_g),clone_tag(current_inv_tag_g,&proposed_inv_tag_g))) //to get out of this loop, we're either carrying or succeeding in populating proposed_* 766 | { 767 | advanced = 1; 768 | newly_advanced = 1; 769 | carry = advance_group(&g->childs[pi], current_tag_u, current_tag_g, current_inv_tag_g); 770 | } 771 | if(newly_advanced) 772 | { 773 | for(int j = i-1; j >= 0; j--) 774 | { 775 | int pj = cache_permute_indices[g->n][g->i+1][j]; 776 | zero_progress_group(&g->childs[pj]); 777 | } 778 | } 779 | if(carry) 780 | { 781 | i++; 782 | if(i == g->n) //tried to pop too far: impossible group 783 | { 784 | zero_progress_group(g); 785 | return 1; 786 | } 787 | else 788 | { 789 | pi = cache_permute_indices[g->n][g->i+1][i]; 790 | //going to advance this next child on the carry; remove its influence 791 | revoke_child_utags(&g->childs[i],¤t_tag_u); //because utags don't stack, it's easy to undo 792 | 793 | //rather than managing stack of gtags, just flush and re-aggregate 794 | clone_tag(og_tag_g,¤t_tag_g); 795 | clone_tag(og_inv_tag_g,¤t_inv_tag_g); 796 | for(int j = g->n-1; j > i; j--) 797 | aggregate_child_gtags(&g->childs[j],¤t_tag_g,¤t_inv_tag_g); 798 | } 799 | } 800 | else 801 | { 802 | current_tag_u = proposed_tag_u; 803 | current_tag_g = proposed_tag_g; 804 | current_inv_tag_g = proposed_inv_tag_g; 805 | i--; 806 | } 807 | } 808 | } 809 | break; 810 | case GROUP_TYPE_CHARS: 811 | carry = 1; 812 | break; 813 | default: //appease compiler 814 | break; 815 | } 816 | zero_progress_modifications(g); 817 | return carry; 818 | } 819 | 820 | void zero_progress_group(group *g) 821 | { 822 | if(g->type == GROUP_TYPE_OPTION) 823 | zero_progress_group(&g->childs[g->i]); 824 | else if(g->type == GROUP_TYPE_SEQUENCE || g->type == GROUP_TYPE_PERMUTE) 825 | { 826 | for(int i = 0; i < g->n; i++) 827 | zero_progress_group(&g->childs[i]); 828 | } 829 | g->i = 0; 830 | } 831 | 832 | void zero_progress_modifications(group *g) 833 | { 834 | modification *m; 835 | for(int i = 0; i < g->n_mods; i++) 836 | { 837 | m = &g->mods[i]; 838 | 839 | if(m->n_injections) { for(int j = 0; j < m->n_injections; j++) m->injection_i[j] = 0; } 840 | if(m->n_smart_substitutions) { for(int j = 0; j < m->n_smart_substitutions; j++) m->smart_substitution_i[j] = j; } 841 | if(m->n_substitutions) { for(int j = 0; j < m->n_substitutions; j++) m->substitution_i[j] = j; } 842 | if(m->n_deletions) { for(int j = 0; j < m->n_deletions; j++) m->deletion_i[j] = 0; } 843 | 844 | if(m->n_injections) { for(int j = 0; j < m->n_injections; j++) m->injection_sub_i[j] = 0; } 845 | if(m->n_smart_substitutions) { for(int j = 0; j < m->n_smart_substitutions; j++) m->smart_substitution_sub_i[j] = 0; } 846 | if(m->n_substitutions) { for(int j = 0; j < m->n_substitutions; j++) m->substitution_sub_i[j] = 0; } 847 | } 848 | } 849 | 850 | --------------------------------------------------------------------------------