/* * ======================================================================= * PROGRAMMER "BAHN, William" * TITLE "Simple Utilty Functions" * CREATED 08 FEB 07 * MODIFIED 08 FEB 07 * FILENAME "dirtyd.c" * ======================================================================= */ /* * ======================================================================= * GENERAL DESCRIPTION * * This file contains many useful functions - and more are added from time * to time. * ======================================================================= */ /* * ======================================================================= * REVISION HISTORY * * REV 2: 02 DEC 03 * Added the GetBoundedInt() function * Added the InBounds() macro * Added the GetDouble() function * REV 1: 28 NOV 03 * Added the PI macro (good to 20 digits) * Added the StripCR() function. * Added the ClearBuffer() function. * Added the WaitForKey() function. * REV 0: 09 NOV 03 * * Initial Creation. * ======================================================================= */ //========================================================================= //== INCLUDE FILES ======================================================== //========================================================================= #include // exit() #include // strlen() #include #include "dirtyd.h" //========================================================================= //== FUNCTION DEFINITIONS ================================================= //========================================================================= //========================================================================= // FUNCTION: PrintHeader() // #include // printf() //========================================================================= #ifdef PROGRAMMER // This function assumes that the #define statements that create these // identifiers are used, typically in the function where main() is defined. // // By checking if one of them is declared, this function can be skipped if // necessary so that the other functions can be used. However, if this // function IS to be available, then it is important that the compiler // encounter the necessary #define statements before this file is included // for the very first time. void PrintHeader(void) { printf("========================================" "=======================================\n"); printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR); printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); printf("Assignment... %s (Rev %i) (Source Code in %s)\n", ASSIGNMENT, REVISION, FILENAME); printf("Description.. %s\n", TITLE); printf(" %s\n", SUBTITLE); printf("========================================" "=======================================\n"); return; } #endif /*========================================================================= * INPUT FUNCTIONS * * In general, the use of scanf() and its sister functions is to be * avoided at nearly all costs. These functions can be quite useful and * certainly have their place, but for the vast majority of users, they * cause for more problems than they are worth. * * The preferred method is to use fgets(), which provides enough * information to permit quite robust input validation. * * char *fgets(char *s, int n, FILE *fp) * * The pointer 's' (which is also the return value of the function) must * point to a writeable string in memory containing at least 'n' bytes. * * The function will read from the input stream 'fp' until either (n-1) * bytes or a newline character has been read from the stream, which ever * comes first. All bytes read from the stream, including the newline * character, are copied to the string pointed to by 's'. The string is * then terminated by a NUL character. * * The reason that the newline character is copied is so that an inspection * of the returned string can determine if the entire line was retrieved * or if there were too many characters to fit into the available string. * * PHILOSOPHY * * Most of the time, Users want to get single items from the keyboard and * if something goes wrong (e.g., the User types a string longer than can * be handled) then it is usually sufficient to make that known to the * program and let the programmer worry about how to deal with it. * * The "input string longer than the input buffer" problem can be dealt * with by using a dynamically allocated buffer that grows to accommodate * the length of the string actually entered. * *========================================================================= */ //========================================================================= //== FUNCTION DEFINITIONS ================================================= //========================================================================= char *fdgets(FILE *fp) { char *s; // Pointer to the dynamically growing string buffer. size_t size; // Present length of the string buffer. size_t len; // Present length of the string in the string buffer. int c; // Character read from the input stream s = NULL; size = 1; // Initial size that will be allocated. len = 0; if (NULL == fp) fp = stdin; while ( (NULL == s) || (NUL != s[len-1]) ) { // Double the buffer size if (2*size < size) // Protect against wrap-around { if (s) { free(s); s = NULL; } return s; } size *= 2; s = (char *) realloc(s, size); if (NULL == s) return s; // Failed to reallocate string buffer // Read in more characters up to the buffer capacity do { c = fgetc(fp); s[len++] = ((EOF == c)||('\n' == c))? NUL : (char) c; } while ( (len < size) && (NUL != s[len-1]) ); } return s; } char fdgetc(FILE *fp) { char *s; char n; s = fdgets(fp); n = 0; if (s) { n = *s; free(s); } return n; } int fdgeti(FILE *fp) { char *s; int n; s = fdgets(fp); n = 0; if (s) { n = atoi(s); free(s); } return n; } long int fdgetl(FILE *fp) { char *s; long int n; s = fdgets(fp); n = 0; if (s) { n = atol(s); free(s); } return n; } float fdgetf(FILE *fp) { char *s; float n; s = fdgets(fp); n = 0; if (s) { n = (float) atof(s); free(s); } return n; } double fdgetd(FILE *fp) { char *s; double n; s = fdgets(fp); n = 0; if (s) { n = atof(s); free(s); } return n; } // Functions that get only from stdin char *dgets(void) { return fdgets(stdin); } char dgetc(void) { return fdgetc(stdin); } int dgeti(void) { return fdgeti(stdin); } long int dgetl(void) { return fdgetl(stdin); } float dgetf(void) { return fdgetf(stdin); } double dgetd(void) { return fdgetd(stdin); } /*========================================================================= * FUNCTION: StripCR() *========================================================================= * This functions strips any trailing Carriage Returns from the end of a * string. In order to catch carriage returns that might be embedded in * the middle of a string, it scans the string from the beginning and looks * for a Line Feed, or a Carriage Return and replaces the first occurance * with a NULL terminator. The use of a do/while() loop allows the test * to operate on the character just examined (and possibly modified) so * that is exits correctly regardless of the NULL terminitor found was * inserted by the loop or was part of the original string. *========================================================================= */ char *StripCR(char *s) { int i; i = -1; do { switch(s[++i]) { case 10: // Line Feed case 13: // Carriage Return s[i] = '\0'; } } while('\0' != s[i]); return(s); } //========================================================================= // FUNCTION: GetFileName() //========================================================================= // This functions gets the a file name from the standard input device and // returns a string pointer to it. There are several modes in which it can // be used. // // The simplest is to pass null arguments for the name and ext variables // and 0 for the size. This tells the function to dynamically allocate // enough memory to accommodate whatever is submitted and to return a // pointer to the allocated memory. // // Example: // // char *filename; // // filename = GetFileName(NULL, NULL, 0); // // The next easiest way is to allocate memory yourself for the string and // tell the function where that memory is located. This is most often done // using a statically allocate character array but previously allocated // dynamic memory will work the same way. Here you MUST tell the function // how much memory is available for the string. The function will ensure // that the string does not exceed the indicated size, including the null // terminator. // // Example: // // char filename[13]; // // GetFileName(filename, NULL, 13); // // If you provide a non-NULL pointer for name and you indicate a size of // zero, the function will assume that the pointer is for previously // allocated memory that is to be freed and then the pointer re-used to // point to new memory. Therefore, do NOT pass the name of a static array // under these conditions as a runtime error will result. // // The ext argument can be used to provide a default file extension. If // the user enters an extension, this parameter will be ignored. If the // user does not include an extension, the one supplied will be appended. // Whether the user entered an extension is determined by checking for the // presence of a period anywhere in the string. // // If the given value for ext is NULL, then no extension will be added // even if the user does not supply one. If the value given for ext is // a pointer to a null string (i.e., ""), then if an extension is not // supplied by the user an empty extension will be added - meaning that // the '.' delimiter will be added but nothing more. char *GetFileName(char *name, char *ext, int size) { int length; char c; int endloop; int extgiven; // Check if size is negative if(0>size) return(NULL); // Check to see if string is static or dynamic if((NULL == name) || (0 == size)) { // String is dynamic length = 0; name = realloc(name, length + 1); name[length] = '\0'; } else { // String is static or fixed length if(size < (int) (strlen(ext)+3) ) // Extension too long, ignore it. ext = NULL; } endloop = FALSE; extgiven = FALSE; while(!endloop) { // Check if there is enough room for another character in string. if( (0 < size) && !(length < size) ) endloop = TRUE; switch(c = getchar()) { case EOF: // End of File found case 10: // Form Feed encountered case 13: // Carriage Return encountered endloop = TRUE; break; case '.': // Extension Delimiter found extgiven = TRUE; default : // All characters (including delimiter above) name = realloc(name, length + 2); name[length++] = c; name[length] = '\0'; break; } } // Check if user supplied an extension and use default if appropriate. if( (!extgiven)&&(NULL != ext) ) { if(0 == size) // dynamic array { // Allocated additional memory for the extension name = realloc(name, length + strlen(ext) + 2); } else // static or fixed length array { // Ensure that the static array can take the extension name[size - strlen(ext) - 2] = '\0'; } strcat(name, "."); strcat(name, ext); } return(name); } //========================================================================= // FUNCTION: OpenAndVerify() // #include // fopen(), FILE, printf() // #include // exit() //========================================================================= FILE *OpenAndVerify(char *name, char *mode) { FILE *fp; fp = fopen(name, mode); if(NULL == fp) { printf("ABORT! - Failed to open file <%s> (mode %s)\n", name, mode); exit(1); } return fp; } //========================================================================= // FUNCTION: rand_int() // #include // rand() //========================================================================= // This function returns a random integer value between min and max // inclusive. int rand_int(int min, int max) { return( rand()%( (max-min) + 1) + min ); } //========================================================================= // FUNCTION: rand_norm() // #include // rand(), RAND_MAX //========================================================================= // This function returns a random floating point value between 0.0 and 1.0 // inclusive. double rand_norm(void) { return( (double)rand()/(double)RAND_MAX ); } //========================================================================= // FUNCTION: rand_fp() //========================================================================= // This function returns a random floating point value between min and max // inclusive. double rand_fp(double min, double max) { return(min + rand_norm()*(max-min)); } //========================================================================= // FUNCTION: ExitIfError() //========================================================================= void ExitIfError(int errcode) { if(errcode) { printf("Abort! (Error #%i detected)\n", errcode); exit(errcode); } return; } //========================================================================= // GetBoundedInt() // This function gets an int from the keyboard and checks if it is within // the specified limits. If it is, then that value is returned, otherwise // the limit that is violated is returned if the mode is set RET_CLIPPED. // Otherwise, the def(ault) value is returned (use RET_DEFAULT). //========================================================================= int GetBoundedInt(int min, int max, int def, int mode) { int i; i = dgeti(); if(i < min) i = (RET_CLIPPED == mode)? min : def; if(i > max) i = (RET_CLIPPED == mode)? max : def; return(i); } //========================================================================= // GetBoundedDouble() // This function gets a double from the keyboard and checks if it is within // the specified limits. If it is, then that value is returned, otherwise // the limit that is violated is returned if the mode is set RET_CLIPPED // Otherwise, the def(ault) value is returned (use RET_DEFAULT). //========================================================================= double GetBoundedDouble(double min, double max, double def, int mode) { double x; x = dgetd(); if(x < min) x = (RET_CLIPPED == mode)? min : def; if(x > max) x = (RET_CLIPPED == mode)? max : def; return(x); } double BoundedDouble(double x, double min, double max, int mode) { if ((DD_CLIP_MINMAX == mode)||(DD_CLIP_MIN == mode)) if (x < min) x = min; if ((DD_CLIP_MINMAX == mode)||(DD_CLIP_MAX == mode)) if (x > max) x = max; return x; } double StringToBoundedDouble(char *s, double def, double min, double max,int mode) { double x; x = (s)? atof(s) : def; return BoundedDouble(x, min, max, mode); } //========================================================================= // GetInt() // This function calls GetBoundedInt with an embedded CLIPPED option. //========================================================================= int GetInt(int min, int max) { return(GetBoundedInt(min, max, 0, RET_CLIPPED)); } //========================================================================= // GetDouble() // This function calls GetBoundedDouble with an embedded CLIPPED option. //========================================================================= double GetDouble(double min, double max) { return(GetBoundedDouble(min, max, 0, RET_CLIPPED)); } //========================================================================= // DYNAMICALLY ALLOCATED ARRAYS //========================================================================= #define MYMEM_MALLOC (0) #define MYMEM_FREE (1) #define MYMEM_CREATE (2) #define MYMEM_DESTROY (3) #define MYMEM_LINES (8192) void *my_memory(FILE *log, void *p, size_t bytes, int action, char *s) { #ifdef MYMEM static FILE *memlog = NULL; static long int Allocations = 0; static long int Deallocations = 0; static long int NetAllocations = 0; static long int MaxAllocations = 0; static long int TotalBytes; static size_t *map_bytes = NULL; static void **map_ptrs = NULL; static int map_entries = 0; int i; #endif switch (action) { case MYMEM_CREATE: #ifdef MYMEM memlog = log; if (memlog) { fprintf(memlog, "%s\n", s); } map_bytes = (size_t *) my_memory(NULL, NULL, MYMEM_LINES*(sizeof(size_t)), MYMEM_MALLOC, "MAP - bytes"); map_ptrs = (void **) my_memory(NULL, NULL, MYMEM_LINES*(sizeof(void *)), MYMEM_MALLOC, "MAP - ptrs"); if (map_bytes && map_ptrs) { for (i = 0; i < MYMEM_LINES; i++) { map_ptrs[i] = NULL; map_bytes[i] = 0; } map_entries = 0; } #else break; #endif break; case MYMEM_MALLOC: p = malloc(bytes); #ifdef MYMEM Allocations++; NetAllocations++; TotalBytes += bytes; if (NetAllocations > MaxAllocations) MaxAllocations = NetAllocations; if (memlog) { fprintf(memlog, "REQUESTED: %6u bytes", bytes); if (p) fprintf(memlog, " [%p]", p); else fprintf(memlog, " [--------] DENIED!"); fprintf(memlog, " Allocs: %10li (%10li net - %10li)", Allocations, NetAllocations, TotalBytes); if (s) fprintf(memlog, " %s", s); fprintf(memlog, "\n"); fflush(memlog); } if (map_bytes && map_ptrs) { if (map_entries < MYMEM_LINES) { map_ptrs[map_entries] = p; map_bytes[map_entries] = bytes; map_entries++; } else { fprintf(memlog, "Pointer Map entry limit exceeded\n"); } } #endif break; case MYMEM_FREE: #ifdef MYMEM Deallocations++; NetAllocations--; if (memlog) { fprintf(memlog, "FREEING..: "); if (p) fprintf(memlog, " [%p]", p); else fprintf(memlog, " [--------] NULL PTR!"); fprintf(memlog, " Deallocs: %10li (%10li net - %10li)", Deallocations, NetAllocations, TotalBytes); if (s) fprintf(memlog, " %s", s); fprintf(memlog, "\n"); fflush(memlog); } if (p) { if (map_bytes && map_ptrs) { for (i = (map_entries-1); (i >= 0) && (p != map_ptrs[i]); i--) ; // EMPTY LOOP; if (i >= 0) { TotalBytes -= map_bytes[i]; map_entries--; while (i < map_entries) { map_ptrs[i] = map_ptrs[i+1]; map_bytes[i] = map_bytes[i+1]; i++; } } else { fprintf(memlog, "Pointer Map entry not found!\n"); } } } #endif if (p) free(p); break; case MYMEM_DESTROY: #ifdef MYMEM if (memlog) { fprintf(memlog, "============================================================\n"); fprintf(memlog, "%s\n", s); fprintf(memlog, "RESIDUAL MEMORY ALLOCATIONS\n"); fprintf(memlog, "============================================================\n"); if (map_bytes && map_ptrs) for (i = 0; i < map_entries; i++) { if (map_ptrs[i] = NULL) fprintf(memlog, "[%p] %li bytes\n", map_ptrs[i], ((long int) map_bytes[i])); } fprintf(memlog, "============================================================\n"); } my_memory(NULL, map_bytes, 0, MYMEM_FREE, "MAP - bytes"); my_memory(NULL, map_ptrs, 0, MYMEM_FREE, "MAP - ptrs"); #else break; #endif break; default: break; } return p; } void free1D(void *p) { my_memory(NULL, p, 0, MYMEM_FREE, "1D"); } void *malloc1D(size_t cols, size_t size) { void *array; size_t bytes; bytes = cols*size; if (0 == bytes) return NULL; array = my_memory(NULL, NULL, bytes, MYMEM_MALLOC, "1D"); return array; } void free2D(void **p, size_t rows) { if(p) while (rows--) if (p[rows]) my_memory(NULL, p[rows], 0, MYMEM_FREE, "2D - row"); my_memory(NULL, p, 0, MYMEM_FREE, "2D - base"); } void **malloc2D(size_t rows, size_t cols, size_t size) { void **array; size_t i; size_t bytes; if (!(rows && cols && size)) return NULL; bytes = rows * sizeof(void*); if (NULL == (array = my_memory(NULL, NULL, bytes, MYMEM_MALLOC, "2D - base"))) return NULL; for (i = 0; i < rows; i++) { if (NULL == ( array[i] = malloc1D(cols, size) )) { while (i) { i--; my_memory(NULL, array[i], 0, MYMEM_FREE, "2D failed - row"); } my_memory(NULL, array, 0, MYMEM_FREE, "2D failed - base"); i = rows; } } return array; } void free3D(void ***p, size_t sheets, size_t rows) { if (p) while (sheets--) if (p[sheets]) free2D(p[sheets], rows); my_memory(NULL, p, 0, MYMEM_FREE, "3D - base"); } void ***malloc3D(size_t sheets, size_t rows, size_t cols, size_t size) { void ***array; size_t i; size_t bytes; if (!(rows && cols && size)) return NULL; bytes = sheets * sizeof(void*); if (NULL == (array = my_memory(NULL, NULL, bytes, MYMEM_MALLOC, "3D - base"))) return NULL; for (i = 0; i < sheets; i++) { if (NULL == ( array[i] = malloc2D(rows, cols, size) )) { while (i) { i--; my_memory(NULL, array[i], 0, MYMEM_FREE, "3D failed - row"); } my_memory(NULL, array, 0, MYMEM_FREE, "3D failed - base"); i = sheets; } } return array; } DWORD Bits2Bytes(DWORD bits) { return (bits / (8*sizeof(BYTE))) + ((bits % (8*sizeof(BYTE)))? 1:0); } BYTE *MemorySet(BYTE *p, DWORD bytes, BYTE v) { DWORD byte; if (p) for (byte = 0; byte < bytes; byte++) p[byte] = v; return p; } BYTE *MemoryCopy(BYTE *dest, BYTE *src, DWORD bytes) { DWORD i; for (i = 0; i < bytes; i++) dest[i] = src[i]; return dest; } void DisplayHEX(FILE *fp, BYTE *p, DWORD bytes, int mode) { DWORD byte; DWORD line; WORD i; switch (mode) { case 0: case 1: default: fprintf(fp, "\n"); fprintf(fp, " -------------------------------------"); fprintf(fp, "---------------------------------------"); fprintf(fp, "\n"); fprintf(fp, " "); for (i = 0; i < 16; i++) { fprintf(fp, "%2X ", i); } fprintf(fp, "- "); for (i = 0; i < 16; i++) { fprintf(fp, "%1X", i); } fprintf(fp, "\n"); fprintf(fp, " -------------------------------------"); fprintf(fp, "---------------------------------------"); fprintf(fp, "\n"); for (line = byte = 0; byte < bytes; line++, byte+=16) { fprintf(fp, " [%06X] ", line); for (i = 0; i < 16; i++) { if (byte+i < bytes) fprintf(fp, "%02X ", p[byte+i]); else fprintf(fp, "-- "); } fprintf(fp, "- "); for (i = 0; i < 16; i++) { if (byte+i < bytes) fprintf(fp, "%1c", (isprint(p[byte+i])? p[byte+i]:'.')); else fprintf(fp, " "); } fprintf(fp, "\n"); } fprintf(fp, " -------------------------------------"); fprintf(fp, "---------------------------------------"); fprintf(fp, "\n"); break; } } WORD GetBitIndex(DWORD bit) { WORD index; index = bit/8; return index; } BYTE GetBitMask(DWORD bit) { BYTE offset; BYTE mask; mask = 0x80; offset = bit%8; mask >>= offset; return mask; } BYTE GetBit(BYTE *d, size_t size, DWORD bit) { WORD index; BYTE mask; index = GetBitIndex(bit); mask = GetBitMask(bit); return (d[index] & mask)? 1 : 0; } void SetBit(BYTE *d, size_t size, DWORD bit, int v) { WORD index; BYTE mask; index = GetBitIndex(bit); mask = GetBitMask(bit); if (v) d[index] |= mask; else d[index] &= ~mask; } typedef struct STRINGPARSER STRINGPARSER; struct STRINGPARSER { char *string; int length; char *next; }; int IsIn(char c, char *s) { int i; for (i = 0; s[i] && (c != s[i]); i++) EMPTYLOOP; return s[i]; } char *ParseString(char *s, char* fdelim, char *tdelim) { static STRINGPARSER *p = NULL; int i, n; char *substring; if (!p) { p = (STRINGPARSER *) malloc(sizeof(STRINGPARSER)); if (p) { p->string = NULL; p->length = 0; p->next = NULL; } else return NULL; } if (NULL == s) { free(p); p = NULL; return NULL; } if (p->string != s) { p->string = s; p->length = strlen(s); p->next = s; } for (; (p->next < (p->string + p->length)) && (IsIn(*(p->next), fdelim)); p->next++) EMPTYLOOP; if ((p->next - p->string) >= p->length) { p->string = NULL; p->length = 0; p->next = NULL; return NULL; } for (n = 0; (p->next+n < (p->string + p->length)) && (!IsIn(*(p->next+n), tdelim)); n++); EMPTYLOOP; substring = malloc(n+1); if (substring) { for (i = 0; i < n; i++) substring[i] = p->next[i]; substring[n] = NUL; } p->next += n; return substring; } DWORD rand_DWORD(DWORD max) { DWORD mask, value; for (mask = 1; mask < max; mask = (mask<<1) + 1) EMPTYLOOP; do { value = (rand()<<(8*sizeof(WORD))) + rand(); value &= mask; } while (value > max); return value; } int memequal(char *s1, char *s2, DWORD bytes) { DWORD i; for (i = 0; i < bytes; i++) if (s1[i] != s2[i]) return FALSE; return TRUE; }