/**************************************************************************** * util.[ch]: Miscellaneous generic utility functions, defs, and macros. * * If you want the error handlers to mention program name, call set_prog_name * at the beginning of your program. ****************************************************************************/ #include #include #include #include #include #include #include "util.h" static const char* prog = ""; /* fgets with dynamic allocation, returning the allocated string, or NULL on * eof or error. Any trailing newline or crlf will be stripped. */ char* dgets(FILE* f) { char* str = NULL; int32 len = 0; int32 old_len; boolean done = FALSE; char chunk[128]; int32 chunk_len; while (!done) { if (!fgets(chunk, 128, f)) { if (ferror(f)) { free(str); return NULL; } else return str; } chunk_len = strlen(chunk); if (chunk[chunk_len-1] == '\n') { chunk[--chunk_len] = '\0'; done = TRUE; } old_len = len; len += chunk_len; str = safe_realloc(str, (len+1) * sizeof(char)); strcpy(str + old_len, chunk); } if (len > 0 && str[len-1] == '\r') str[len-1] = '\0'; return str; } /* sprintf with dynamic allocation, returning the allocated string. */ char* dsprintf(const char* format, ...) { va_list args; char* str = NULL; va_start(args, format); str = vdsprintf(format, args); va_end(args); return str; } /* Append src to the dynamically allocated string dest, reallocing to make room. Return the * new dest. */ char* dstrcat(char** dest, const char* src) { *dest = safe_realloc(*dest, (strlen(*dest) + strlen(src) + 1) * sizeof(char)); strcat(*dest, src); return *dest; } /* Return true if the given string is composed entirely of whitespace, or is the empty string. */ boolean is_blank(const char* str) { const char* p; for (p=str; *p; p++) { if (!isspace(*p)) return FALSE; } return TRUE; } /* Return true if the given string is composed entirely of hexadecimal digits (upper or lowercase). * An empty string is considered non-hexadecimal. */ boolean is_hexadecimal(const char* str) { const char* p; if (!(*str)) return FALSE; for (p=str; *p; p++) { if (!isxdigit(*p)) return FALSE; } return TRUE; } /* Return true if the given string is composed entirely of numeric digits. An empty string is * considered non-numeric. */ boolean is_numeric(const char* str) { const char* p; if (!(*str)) return FALSE; for (p=str; *p; p++) { if (!isdigit(*p)) return FALSE; } return TRUE; } /* Count and return the number of decimal digits in the given positive number. */ int32 num_digits(uint32 num) { int32 count = 1; while (num >= 10) { num /= 10; count++; } return count; } /* Calloc, aborting if out of memory */ void* safe_calloc(size_t count, size_t element_size) { void* mem; mem = calloc(count, element_size); if (!mem && count > 0) error_abort("out of memory!"); return mem; } /* Malloc, aborting if out of memory */ void* safe_malloc(size_t len) { void* mem; mem = malloc(len); if (!mem && len > 0) error_abort("out of memory!"); return mem; } /* Realloc, aborting if out of memory */ void* safe_realloc(void* mem, size_t len) { mem = realloc(mem, len); if (!mem && len > 0) error_abort("out of memory!"); return mem; } /* Strdup, aborting if out of memory */ char* safe_strdup(const char* str) { char* new_str; new_str = safe_malloc((strlen(str)+1) * sizeof(char)); strcpy(new_str, str); return new_str; } /* Remove whitespace from the beginning and end of str */ void trim_whitespace(char* str) { char* c; int32 i; for (c=str; *c && isspace(*c); c++) {} memmove(str, c, strlen(c)+1); for (i=strlen(str)-1; i >= 0 && isspace(str[i]); i--) {} str[i+1] = '\0'; } /* vsprintf with dynamic allocation, returning the allocated string. */ char* vdsprintf(const char* format, va_list args) { char* str = NULL; int32 buflen = 128; int32 nchars; while (1) { str = safe_realloc(str, buflen * sizeof(char)); nchars = vsnprintf(str, buflen, format, args); if (nchars < 0) /* pre-glibc 2.1 method */ buflen *= 2; else if (nchars >= buflen) /* post-glibc 2.1 method */ buflen = nchars + 1; else break; } return str; } /* Error Handlers */ /* Print a formatted warning to stderr */ void warn(const char* format, ...) { va_list args; fprintf(stderr, "%s warning: ", prog); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); } /* Abort with a formatted error message */ void error_abort(const char* format, ...) { va_list args; fprintf(stderr, "%s dying: ", prog); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); exit(-1); } /* Print a formatted warning to stderr, including the system error string */ void sys_warn(const char *format, ...) { va_list args; fprintf(stderr, "%s warning: ", prog); va_start(args, format); vfprintf(stderr, format, args); fprintf(stderr, " (%s)\n", strerror(errno)); va_end(args); } /* Abort with a formatted error message, including the system error string */ void sys_error_abort(const char* format, ...) { va_list args; fprintf(stderr, "%s dying: ", prog); va_start(args, format); vfprintf(stderr, format, args); fprintf(stderr, " (%s)\n", strerror(errno)); va_end(args); exit(-1); } /* Set the program name. Call this at the beginning of your program if you * want the error handlers to mention program name. */ void set_prog_name(const char* name) { prog = name; }