/*++ Copyright (c) 1998 Microsoft Corporation Module Name: hashgen.cpp Abstract: Table Generator for hhead.cxx, which contains all known HTTP headers for wininet project. This is also the location where all known HTTP headers must be added. Author: Arthur Bierer (arthurbi) 12-Jan-1998 Revision History: --*/ // // Instructions for adding new HTTP header: // 1. Update wininet.w and rebuild wininet.h with new HTTP_QUERY_ code // 2. Add/Edit header to this file/program, hashgen.cpp with the // new header string (see Items[] array below) // 3. Compile new hashgen.exe, Execute with -o, write down a good seed // note that this may take all night to find a good seed which // give a nice smaller table size. (note this can be skipped if // you just need a quick table for dev purposes) // 4. Re-Execute hashgen.exe with -b# set with your seed to generate // hhead.cxx // 5. Transfer new hhead.cxx file to wininet\http // 6. Update const defines MAX_HEADER_HASH_SIZE and HEADER_HASH_SEED // from new hhead.cxx to wininet\http\headers.h // 7. Transfer and checkin hashgen.cpp, wininet.w, headers,h, hhead.cxx // in their appropriate directories. // // // Includes... // #include #include #include #include #include // // macros // #define IS_ARG(c) ((c) == '-') #define DIM(x) (sizeof(x) / sizeof(x[0])) #define ENUMDEF(x, y) ,x, #x, #y #define OUTPUT_CODE_FILE "hhead.cxx" #define MAX_SIZE_HASHARRAY_TO_ATTEMPT 600 #define UNKNOWN_HASH_ENTRY 0 // character to put in array when when its not valid // // Items - This is the array that must be edited for Wininet to process new // HTTP headers // // Things to keep in mind before you add to this array // 1. Headers are Alphatized for convience sake // 2. All NULL entries MUST be at the end of the array // 3. All HTTP_QUERY_* codes in wininet.h MUST have an entry even if they are not strings // 4. Entries are as follows: // header string, HTTP_QUERY_* code in wininet.h, flags used in wininet\http\query.cxx // 5. All entries must be in lowercase. // struct Item { char *ptok; DWORD id; char *pidName; char *pFlagsName; } Items[] = { { "Accept" ENUMDEF(HTTP_QUERY_ACCEPT, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Accept-Charset" ENUMDEF(HTTP_QUERY_ACCEPT_CHARSET, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Accept-Encoding" ENUMDEF(HTTP_QUERY_ACCEPT_ENCODING, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Accept-Language" ENUMDEF(HTTP_QUERY_ACCEPT_LANGUAGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Accept-Ranges" ENUMDEF(HTTP_QUERY_ACCEPT_RANGES, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Age" ENUMDEF(HTTP_QUERY_AGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Allow" ENUMDEF(HTTP_QUERY_ALLOW, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Authorization" ENUMDEF(HTTP_QUERY_AUTHORIZATION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Cache-Control" ENUMDEF(HTTP_QUERY_CACHE_CONTROL, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Connection" ENUMDEF(HTTP_QUERY_CONNECTION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Base" ENUMDEF(HTTP_QUERY_CONTENT_BASE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Description" ENUMDEF(HTTP_QUERY_CONTENT_DESCRIPTION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Disposition" ENUMDEF(HTTP_QUERY_CONTENT_DISPOSITION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Encoding" ENUMDEF(HTTP_QUERY_CONTENT_ENCODING, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Id" ENUMDEF(HTTP_QUERY_CONTENT_ID, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Language" ENUMDEF(HTTP_QUERY_CONTENT_LANGUAGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Length" ENUMDEF(HTTP_QUERY_CONTENT_LENGTH, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_NUMBER)) }, { "Content-Location" ENUMDEF(HTTP_QUERY_CONTENT_LOCATION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Md5" ENUMDEF(HTTP_QUERY_CONTENT_MD5, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Range" ENUMDEF(HTTP_QUERY_CONTENT_RANGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Transfer-Encoding" ENUMDEF(HTTP_QUERY_CONTENT_TRANSFER_ENCODING, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Content-Type" ENUMDEF(HTTP_QUERY_CONTENT_TYPE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Cookie" ENUMDEF(HTTP_QUERY_COOKIE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Cost" ENUMDEF(HTTP_QUERY_COST, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Date" ENUMDEF(HTTP_QUERY_DATE, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "Derived-From" ENUMDEF(HTTP_QUERY_DERIVED_FROM, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Etag" ENUMDEF(HTTP_QUERY_ETAG, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Expect" ENUMDEF(HTTP_QUERY_EXPECT, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "Expires" ENUMDEF(HTTP_QUERY_EXPIRES, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Forwarded" ENUMDEF(HTTP_QUERY_FORWARDED, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "From" ENUMDEF(HTTP_QUERY_FROM, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Host" ENUMDEF(HTTP_QUERY_HOST, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "If-Modified-Since" ENUMDEF(HTTP_QUERY_IF_MODIFIED_SINCE, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "If-Match" ENUMDEF(HTTP_QUERY_IF_MATCH, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "If-None-Match" ENUMDEF(HTTP_QUERY_IF_NONE_MATCH, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "If-Range" ENUMDEF(HTTP_QUERY_IF_RANGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "If-Unmodified-Since" ENUMDEF(HTTP_QUERY_IF_UNMODIFIED_SINCE, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "Last-Modified" ENUMDEF(HTTP_QUERY_LAST_MODIFIED, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "Link" ENUMDEF(HTTP_QUERY_LINK, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Location" ENUMDEF(HTTP_QUERY_LOCATION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Mime-Version" ENUMDEF(HTTP_QUERY_MIME_VERSION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Max-Forwards" ENUMDEF(HTTP_QUERY_MAX_FORWARDS, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Message-id" ENUMDEF(HTTP_QUERY_MESSAGE_ID, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Ms-Echo-Request" ENUMDEF(HTTP_QUERY_ECHO_REQUEST, 0) }, { "Ms-Echo-Reply" ENUMDEF(HTTP_QUERY_ECHO_REPLY, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Orig-Uri" ENUMDEF(HTTP_QUERY_ORIG_URI, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Pragma" ENUMDEF(HTTP_QUERY_PRAGMA, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Proxy-Authenticate" ENUMDEF(HTTP_QUERY_PROXY_AUTHENTICATE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Proxy-Authorization" ENUMDEF(HTTP_QUERY_PROXY_AUTHORIZATION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Proxy-Connection" ENUMDEF(HTTP_QUERY_PROXY_CONNECTION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Proxy-Support" ENUMDEF(HTTP_QUERY_PROXY_SUPPORT, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Public" ENUMDEF(HTTP_QUERY_PUBLIC, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Range" ENUMDEF(HTTP_QUERY_RANGE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Referer" ENUMDEF(HTTP_QUERY_REFERER, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Refresh" ENUMDEF(HTTP_QUERY_REFRESH, 0) }, { "Retry-After" ENUMDEF(HTTP_QUERY_RETRY_AFTER, (HTTP_QUERY_FLAG_REQUEST_HEADERS | HTTP_QUERY_FLAG_SYSTEMTIME)) }, { "Server" ENUMDEF(HTTP_QUERY_SERVER, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Set-Cookie" ENUMDEF(HTTP_QUERY_SET_COOKIE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Title" ENUMDEF(HTTP_QUERY_TITLE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Transfer-Encoding" ENUMDEF(HTTP_QUERY_TRANSFER_ENCODING, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Unless-Modified-Since" ENUMDEF(HTTP_QUERY_UNLESS_MODIFIED_SINCE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Upgrade" ENUMDEF(HTTP_QUERY_UPGRADE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Uri" ENUMDEF(HTTP_QUERY_URI, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "User-Agent" ENUMDEF(HTTP_QUERY_USER_AGENT, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Vary" ENUMDEF(HTTP_QUERY_VARY, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Via" ENUMDEF(HTTP_QUERY_VIA, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Warning" ENUMDEF(HTTP_QUERY_WARNING, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "WWW-Authenticate" ENUMDEF(HTTP_QUERY_WWW_AUTHENTICATE, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "Authentication-Info" ENUMDEF(HTTP_QUERY_AUTHENTICATION_INFO, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "PassportURLs" ENUMDEF(HTTP_QUERY_PASSPORT_URLS, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { "PassportConfig" ENUMDEF(HTTP_QUERY_PASSPORT_CONFIG, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, // NULL strs must be in end of array { NULL ENUMDEF(HTTP_QUERY_VERSION, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { NULL ENUMDEF(HTTP_QUERY_STATUS_CODE, HTTP_QUERY_FLAG_NUMBER) }, { NULL ENUMDEF(HTTP_QUERY_STATUS_TEXT, 0) }, { NULL ENUMDEF(HTTP_QUERY_RAW_HEADERS, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { NULL ENUMDEF(HTTP_QUERY_RAW_HEADERS_CRLF, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { NULL ENUMDEF(HTTP_QUERY_REQUEST_METHOD, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { NULL ENUMDEF(HTTP_QUERY_ECHO_HEADERS, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, { NULL ENUMDEF(HTTP_QUERY_ECHO_HEADERS_CRLF, HTTP_QUERY_FLAG_REQUEST_HEADERS) }, }; // // Declarations of common strings used in creating output "C" file // char szFileHeader[] = {"/*++\n\n" "Copyright (c) 1997 Microsoft Corporation\n\n" "Module Name:\n\n" " " OUTPUT_CODE_FILE "\n\n" "Abstract:\n\n" " This file contains autogenerated table values of a perfect hash function\n" " DO NOT, DO NOT EDIT THIS FILE, TO ADD HEADERS SEE hashgen.cpp\n" " Contents:\n" " GlobalKnownHeaders\n" " GlobalHeaderHashs\n\n" "Author:\n\n" " Arthur Bierer (arthurbi) 19-Dec-1997 (AND) my code generator[hashgen.exe]\n\n" "Revision History:\n\n" "--*/\n\n\n" }; char szComment1[] = { "//\n" "// GlobalHeaderHashs - array of precalculated hashes on case-sensetive set of known headers.\n" "// This array must be used with the same hash function used to generate it.\n" "// Note, all entries in this array are biased (++'ed) by 1 from HTTP_QUERY_ manifests in wininet.h.\n" "// 0-ed entries indicate error values\n" "//\n\n" }; char szComment2[] = { "//\n" "// GlobalKnownHeaders - array of HTTP request and response headers that we understand.\n" "// This array must be in the same order as the HTTP_QUERY_ manifests in WININET.H\n" "//\n\n" "#define HEADER_ENTRY(String, Flags, HashVal) String, sizeof(String) - 1, Flags, HashVal\n\n" }; char szDef1[] = { "#ifdef HEADER_HASH_SEED\n" "#if (HEADER_HASH_SEED != %u)\n" "#error HEADER_HASH_SEED has not been updated in the header file, please copy this number to the header\n" "#endif\n" "#else\n" "#define HEADER_HASH_SEED %u\n" "#endif\n\n" }; char szDef2[] = { "#ifdef MAX_HEADER_HASH_SIZE\n" "#if (MAX_HEADER_HASH_SIZE != %u)\n" "#error MAX_HEADER_HASH_SIZE has not been updated in the header file, please copy this number to the header\n" "#endif\n" "#else\n" "#define MAX_HEADER_HASH_SIZE %u\n" "#endif\n\n" }; char szDef3[] = { "#ifdef HTTP_QUERY_MAX\n" "#if (HTTP_QUERY_MAX != %u)\n" "#error HTTP_QUERY_MAX is not the same as the value used in wininet.h, this indicates mismatched headers, see hashgen.cpp\n" "#endif\n" "#endif\n\n" }; char szIncludes[] = { "#include \n" "#include \"httpp.h\"\n\n" }; // // Hash - function used to create table, // THIS FUNCTION MUST BE THE SAME AS THE ONE USED in WININET // DWORD Hash(char *pszName, DWORD j, DWORD seed) { DWORD hash = seed; while (*pszName) { hash += (hash << 5) + *pszName++; } return (j==0) ? hash : hash % j; } // // CompareItems - a util function for qsort-ing by ID for table creation // in the output file // int __cdecl CompareItems (const void *elem1, const void *elem2 ) { const struct Item *pItem1, *pItem2; pItem1 = (struct Item *) elem1; pItem2 = (struct Item *) elem2; if ( pItem1->id < pItem2->id ) { return -1; } else if ( pItem1->id > pItem2->id ) { return 1; } return 0; } // // usage() - print out our usage instructions to command line // void usage() { fprintf(stderr, "\n" "usage: hashgen [-m[#]] [-b[#]] [-t[#]] [-o] [-p] [-f]\n" "\n" "where: -m[#] = Max hash table size to test with, default = 600\n" " -b[#] = Starting hash seed, default = 0\n" " -t[#] = Threshold of table size to halt search at, default = 200\n" " -o = Enable optimal exhaustive search mode (can take 24+ hrs)\n" " -p = Path used for output generation\n" " -f = Output filename, \"hhead.cxx\" is assumed\n" "\n" "Instructions for adding new HTTP header:\n" "\t1. Update wininet.w and rebuild wininet.h with new HTTP_QUERY_ code\n" "\t2. Add/Edit this file/program, hashgen.cpp with the new header string\n" "\t3. Compile/Execute new hashgen.exe with -o, write down a good seed\n" "\t4. Re-Execute hashgen.exe with -b# set with your seed to generate\n" "\t hhead.cxx\n" "\t5. Transfer new hhead.cxx file to wininet\\http\n" "\t6. Update const defines MAX_HEADER_HASH_SIZE and HEADER_HASH_SEED\n" "\t from new hhead.cxx to wininet\\http\\headers.h\n" "\t7. Transfer and checkin hashgen.cpp, wininet.w, headers,h, hhead.cxx\n" ); exit(1); } // // MakeMeLower - Makes a lower case string using a static 255 byte array // LPSTR MakeMeLower( IN LPSTR lpszMixedCaseStr ) { static CHAR szLowerCased[256]; if ( lstrlen(lpszMixedCaseStr) > 255 ) { fprintf(stderr, "Internal error: an HTTP header is too long\n\n"); return szLowerCased; } lstrcpy( szLowerCased, lpszMixedCaseStr ); CharLower(szLowerCased); return szLowerCased; } // // main - where it all gets done !!!! // void __cdecl //_CRTAPI1 main( int argc, char * argv[] ) { DWORD nMax = MAX_SIZE_HASHARRAY_TO_ATTEMPT; DWORD dwBestNumber = 0, dwBestSeed = 0 /*349160*/ /*4458*//*202521*/; DWORD dwSearchThreshold = 200; BOOL bFoundOne = FALSE; BOOL bFindOptimalSeed = FALSE; LPSTR szPath = ""; LPSTR szFileName = OUTPUT_CODE_FILE; DWORD i, j, k; DWORD dwValidStringsInArray = 0; DWORD *pHash = new DWORD[nMax]; for (--argc, ++argv; argc; --argc, ++argv) { if (IS_ARG(**argv)) { switch (*++*argv) { case '?': usage(); break; case 'm': nMax = (DWORD)atoi(++*argv); break; case 'b': dwBestSeed = (DWORD)atoi(++*argv); break; case 't': dwSearchThreshold = (DWORD)atoi(++*argv); break; case 'p': szPath = ++*argv; break; case 'f': szFileName = ++*argv; break; case 'o': bFindOptimalSeed = TRUE; break; default: fprintf(stderr,"error: unrecognized command line flag: '%c'\n", **argv); usage(); } } else { fprintf(stderr,"error: unrecognized command line argument: \"%s\"\n", *argv); usage(); } } // // Let the Work begin... // dwBestNumber = nMax; if (bFindOptimalSeed) { printf("This will take a while, perhaps all night(consider a Ctrl-C)...\n"); } for (i = 0; i < DIM(Items); i++ ) { if ( Items[i].ptok ) dwValidStringsInArray++; } for (i = dwBestSeed; i < (~0); i++) { //printf("%d,\n", i); for (j = dwValidStringsInArray; j < nMax; j++) { memset (pHash, UNKNOWN_HASH_ENTRY, nMax * sizeof(DWORD)); for (k = 0; k < dwValidStringsInArray; k++) { DWORD HashNow = Hash(MakeMeLower(Items[k].ptok), j, i) /*% j(table_size), i(seed)*/; if ( HashNow > j ) { fprintf(stderr, "Error, Error - exceed table size, bad hash alg\n"); break; } if (pHash[HashNow] != UNKNOWN_HASH_ENTRY) break; else { pHash[HashNow] = Items[k].id+1; } } if ( k == dwValidStringsInArray ) { //printf( "Found one with hash_size=%d, seed=%u...\n", j,i ); bFoundOne = TRUE; goto found_one; } } found_one: if ( bFoundOne ) { if (j < dwBestNumber) { dwBestNumber = j; dwBestSeed = i; printf("Found a New One, hashtable_size=%d, seed=%u...\n", j ,i); if ( !bFindOptimalSeed && dwBestNumber < dwSearchThreshold ) { goto stop_search; } } bFoundOne = FALSE; } } stop_search: if ( dwBestNumber < nMax && dwBestNumber == j) { printf("Generating %s which contains, perfect hash for known headers\n", OUTPUT_CODE_FILE); FILE *f; CHAR szOutputFileAndPath[512]; strcpy(szOutputFileAndPath, szPath); strcat(szOutputFileAndPath, szFileName); f = fopen(szOutputFileAndPath, "w"); if ( f == NULL ) { fprintf(stderr, "Err: Could Not Open %s for writing\n", szOutputFileAndPath); exit(-1); } fprintf(f, szFileHeader); // print header fprintf(f, szIncludes); // includes fprintf(f, szDef1, dwBestSeed, dwBestSeed); fprintf(f, szDef2, dwBestNumber, dwBestNumber); fprintf(f, szDef3, HTTP_QUERY_MAX); fprintf(f, szComment1); // print comment if ( dwBestNumber < 255 ) { fprintf(f, "const BYTE GlobalHeaderHashs[MAX_HEADER_HASH_SIZE] = {\n"); } else { fprintf(f, "const WORD GlobalHeaderHashs[MAX_HEADER_HASH_SIZE] = {\n"); } DWORD col = 0; // // spit our Nicely calculated perfect hash table.. // for ( i = 0; i < dwBestNumber; i++ ) { col++; if ( col == 1 ) { fprintf(f, " "); } fprintf(f, "%3u, ", (BYTE) pHash[i]); if ( col == 6 ) { fprintf(f, "\n"); col = 0; } } fprintf(f, "\n };\n\n"); // // Now spit our KnownHeader array... // qsort(Items, DIM(Items), sizeof(Items[0]), CompareItems); fprintf(f, szComment2); if ( DIM(Items) != (HTTP_QUERY_MAX+1) ) { fprintf(stderr, "ERROR, HTTP_QUERY_MAX the wrong size,( different wininet.h's? )\n"); return; } fprintf(f, "const struct KnownHeaderType GlobalKnownHeaders[HTTP_QUERY_MAX+1] = {\n"); for (j = 0; j < DIM(Items); j++) { char szBuffer[256]; DWORD dwHash = 0; sprintf(szBuffer, " HEADER_ENTRY(\"%s\",", (Items[j].ptok ? Items[j].ptok : "\0")); if ( Items[j].ptok ) { dwHash = Hash(MakeMeLower(Items[j].ptok), 0, dwBestSeed); } fprintf(f, "%-45s %s, 0x%X),\n", szBuffer, Items[j].pFlagsName, dwHash); } fprintf(f," };\n\n\n"); fclose(f); } else { fprintf(stderr, "Error, could not find an ideal number\n"); } }