# HG changeset patch # User Matti Hamalainen # Date 1543314066 -7200 # Node ID 4109967ef832505dce8626492acc05d98584f80e # Parent 8a9ef75fd3cd223befdbf824e99c594144d4b905# Parent 2b961e6b35e5890b8dceaaefc83d5226957a0e12 Merge. diff -r 2b961e6b35e5 -r 4109967ef832 src/dmlib.h --- a/src/dmlib.h Thu Nov 08 11:45:13 2018 +0200 +++ b/src/dmlib.h Tue Nov 27 12:21:06 2018 +0200 @@ -408,14 +408,30 @@ void * dmMemset(void *ptr, const int c, size_t n); #endif + +/** String trimming option flags for dm_strdup_trim() + */ +enum +{ + DM_TRIM_START = 1, + DM_TRIM_END = 2, + DM_TRIM_BOTH = 3 +}; + + + char * dm_strdup(const char *str); char * dm_strndup(const char *str, const size_t n); +char * dm_strdup_trim(const char *src, const int flags); +char * dm_strndup_trim(const char *src, const size_t n, const int flags); char * dm_strdup_vprintf_len(const char *fmt, va_list args, int *len); char * dm_strdup_vprintf(const char *fmt, va_list args); char * dm_strdup_printf(const char *fmt, ...); char * dm_basefilename(const char *filename); char * dm_strdup_fext(const char *filename, const char *fmt); +int dm_strcasecmp(const char *haystack, const char *needle); +int dm_strncasecmp(const char *haystack, const char *needle, size_t n); char * dm_strrcasecmp(char *str, const char *needle); BOOL dmGetIntVal(const char *str, unsigned int *value, BOOL *neg); diff -r 2b961e6b35e5 -r 4109967ef832 src/dmstring.c --- a/src/dmstring.c Thu Nov 08 11:45:13 2018 +0200 +++ b/src/dmstring.c Tue Nov 27 12:21:06 2018 +0200 @@ -42,6 +42,53 @@ } +/* Compare two strings ignoring case [strcasecmp, strncasecmp] + */ +int dm_strcasecmp(const char *haystack, const char *needle) +{ + const char *s1 = haystack, *s2 = needle; + assert(haystack != NULL); + assert(needle != NULL); + + if (haystack == needle) + return 0; + + while (*s1 && *s2) + { + int k = tolower(*s1) - tolower(*s2); + if (k != 0) + return k; + s1++; + s2++; + } + + return 0; +} + + +int dm_strncasecmp(const char *haystack, const char *needle, size_t n) +{ + const char *s1 = haystack, *s2 = needle; + assert(haystack != NULL); + assert(needle != NULL); + + if (haystack == needle) + return 0; + + while (n > 0 && *s1 && *s2) + { + int k = tolower(*s1) - tolower(*s2); + if (k != 0) + return k; + s1++; + s2++; + n--; + } + + return 0; +} + + /* Check if end of the given string str matches needle * case-insensitively, return pointer to start of the match, * if found, NULL otherwise. @@ -58,7 +105,7 @@ if (slen < nlen) return NULL; - if (strcasecmp(str - nlen - 1, needle) == 0) + if (dm_strcasecmp(str - nlen - 1, needle) == 0) return str - nlen - 1; else return NULL; @@ -83,26 +130,85 @@ /* Implementation of strndup() with NULL check */ -char *dm_strndup(const char *s, const size_t n) +char *dm_strndup(const char *str, const size_t n) { char *res; - if (s == NULL) + if (str == NULL) return NULL; - size_t len = strlen(s); + size_t len = strlen(str); if (len > n) len = n; if ((res = dmMalloc(len + 1)) == NULL) return NULL; - memcpy(res, s, len); + memcpy(res, str, len); res[len] = 0; return res; } +/* Like strdup, but trims whitespace from the string according to specified flags. + * See DM_TRIM_* in dmlib.h. If the resulting string would be empty (length 0), + * NULL is returned. + */ +static char * dm_strdup_trim_do(const char *src, size_t len, const int flags) +{ + char *res; + size_t start, end; + + if (len == 0) + return NULL; + + // Trim start: find first non-whitespace character + if (flags & DM_TRIM_START) + for (start = 0; start < len && isspace(src[start]); start++); + else + start = 0; + + // Trim end: find last non-whitespace character + if (flags & DM_TRIM_END) + for (end = len - 1; end > start && isspace(src[end]); end--); + else + end = len; + + // Allocate memory for result + if (src[end] == 0 || isspace(src[end])) + return NULL; + + len = end - start + 1; + if ((res = dmMalloc(len + 1)) == NULL) + return NULL; + + memcpy(res, src + start, len); + res[len] = 0; + return res; +} + + +char *dm_strdup_trim(const char *src, const int flags) +{ + if (src == NULL) + return NULL; + + return dm_strdup_trim_do(src, strlen(src), flags); +} + + +char *dm_strndup_trim(const char *src, const size_t n, const int flags) +{ + size_t len; + if (src == NULL || n == 0) + return NULL; + + for (len = 0; len < n && src[len]; len++); + + return dm_strdup_trim_do(src, len, flags); +} + + /* Simulate a sprintf() that allocates memory */ char *dm_strdup_vprintf_len(const char *fmt, va_list args, int *len) diff -r 2b961e6b35e5 -r 4109967ef832 tools/fanalyze.c --- a/tools/fanalyze.c Thu Nov 08 11:45:13 2018 +0200 +++ b/tools/fanalyze.c Tue Nov 27 12:21:06 2018 +0200 @@ -10,8 +10,9 @@ #include "dmargs.h" #include "dmfile.h" -#define SET_MAX_FILES 16 +#define SET_MAX_FILES 64 #define SET_MAX_ELEMS 256 +#define SET_MAX_VALUES 32 /* Typedefs @@ -47,11 +48,50 @@ } DMSourceFile; +enum +{ + DMGV_uint8 = 0, + DMGV_uint16_le, + DMGV_uint16_be, + DMGV_uint32_le, + DMGV_uint32_be, + + DMGV_last +}; + + +typedef struct +{ + char *name; + uint32_t nmax; + unsigned int bsize; +} DMGrepDef; + + +static const DMGrepDef dmGrepTypes[DMGV_last] = +{ + { "8bit (byte)" , (1UL << 8) - 1, 1 }, + { "16bit (word) LE" , (1UL << 16) - 1, 2 }, + { "16bit (word) BE" , (1UL << 16) - 1, 2 }, + { "32bit (word) LE" , (1UL << 32) - 1, 4 }, + { "32bit (word) BE" , (1UL << 32) - 1, 4 }, +}; + + +typedef struct +{ + int type; + uint32_t value; +} DMGrepValue; + + /* Global variables */ -int nsrcFiles = 0; // Number of source files -DMSourceFile srcFiles[SET_MAX_FILES]; // Source file names -DMStats totalStats; +int nsrcFiles = 0; // Number of source files +DMSourceFile srcFiles[SET_MAX_FILES]; // Source file names +DMStats totalStats; +int nsetGrepValues = 0; +DMGrepValue setGrepValues[SET_MAX_VALUES]; /* Arguments @@ -60,6 +100,7 @@ { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 2, 'g', "grep", "Binary grep mode [,[8|16|32]]", OPT_ARGREQ }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); @@ -72,24 +113,145 @@ } +int argParseGrepValue(const char *arg) +{ + const char *sep = strchr(arg, ','); + char *vspec, *vstr; + int vtype = -1, ret = DMERR_OK; + uint32_t vval; + + // Do we have spec? + if (sep != NULL) + { + vspec = dm_strdup_trim(sep + 1, DM_TRIM_BOTH); + vstr = dm_strndup_trim(arg, sep - arg, DM_TRIM_BOTH); + } + else + { + vspec = NULL; + vstr = dm_strdup(arg); + } + + // Parse spec if any + if (vspec != NULL) + { + BOOL vendianess = TRUE; + char *vtmp = vspec; + + // Get endianess specifier, if any + if (dm_strncasecmp(vtmp, "le", 2) == 0) + { + vendianess = TRUE; + vtmp += 2; + } + else + if (dm_strncasecmp(vtmp, "be", 2) == 0) + { + vendianess = FALSE; + vtmp += 2; + } + + // Get value bit size + if (strcmp(vtmp, "8") == 0) + vtype = DMGV_uint8; + else + if (strcmp(vtmp, "16") == 0) + vtype = vendianess ? DMGV_uint16_le : DMGV_uint16_be; + else + if (strcmp(vtmp, "32") == 0) + vtype = vendianess ? DMGV_uint32_le : DMGV_uint32_be; + else + { + ret = dmError(DMERR_INVALID_ARGS, + "Invalid grep type '%s'.\n", + vspec); + goto out; + } + } + + // Get value + if (!dmGetIntVal(vstr, &vval, NULL)) + { + ret = dmError(DMERR_INVALID_ARGS, + "Not a valid integer value '%s'.\n", + vstr); + goto out; + } + + // Check if we need to guess size + if (vtype < 0) + { + for (int n = DMGV_last; n >= 0; n--) + { + const DMGrepDef *def = &dmGrepTypes[n]; + if (vval <= def->nmax) + vtype = n; + } + } + + if (vtype < 0) + { + ret = dmError(DMERR_INVALID_ARGS, + "Could not guess value type for '%s'.\n", + arg); + goto out; + } + + // Check range + if (vval > dmGrepTypes[vtype].nmax) + { + ret = dmError(DMERR_INVALID_ARGS, + "Integer value %d <= %d <= %d out of range for type %s.\n", + vval, 0, dmGrepTypes[vtype].nmax, dmGrepTypes[vtype].name); + + goto out; + } + + if (nsetGrepValues < SET_MAX_VALUES) + { + DMGrepValue *node = &setGrepValues[nsetGrepValues++]; + node->type = vtype; + node->value = vval; + + dmMsg(1, "Grep value %s : %d / 0x%x\n", + dmGrepTypes[vtype].name, + vval, vval); + } + else + { + ret = dmError(DMERR_BOUNDS, + "Too many greps specified (max %d).", + SET_MAX_VALUES); + } + +out: + dmFree(vspec); + dmFree(vstr); + return ret; +} + + BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { (void) optArg; switch (optN) { - case 0: - argShowHelp(); - exit(0); - break; + case 0: + argShowHelp(); + exit(0); + break; - case 1: - dmVerbosity++; - break; + case 1: + dmVerbosity++; + break; - default: - dmErrorMsg("Unknown argument '%s'.\n", currArg); - return FALSE; + case 2: + return argParseGrepValue(optArg) == DMERR_OK; + + default: + dmErrorMsg("Unknown argument '%s'.\n", currArg); + return FALSE; } return TRUE; @@ -188,6 +350,58 @@ dmInitStats(&file->stats); } + + // Check if we are in grep mode + if (nsetGrepValues > 0) + { + for (int nfile = 0; nfile < nsrcFiles; nfile++) + { + DMSourceFile *file = &srcFiles[nfile]; + dmPrint(0, "\n%s:\n", file->filename); + + for (int n = 0; n < nsetGrepValues; n++) + { + DMGrepValue *node = &setGrepValues[n]; + const DMGrepDef *def = &dmGrepTypes[node->type]; + + for (size_t offs = 0; offs + def->bsize < file->size; offs++) + { + uint32_t mval = -1; + switch (node->type) + { + case DMGV_uint8: + mval = *((uint8_t *) (file->data + offs)); + break; + + case DMGV_uint16_le: + mval = DM_LE16_TO_NATIVE(*((uint16_t *) (file->data + offs))); + break; + + case DMGV_uint16_be: + mval = DM_BE16_TO_NATIVE(*((uint16_t *) (file->data + offs))); + break; + + case DMGV_uint32_le: + mval = DM_LE32_TO_NATIVE(*((uint32_t *) (file->data + offs))); + break; + + case DMGV_uint32_be: + mval = DM_BE32_TO_NATIVE(*((uint32_t *) (file->data + offs))); + break; + } + + if (mval == node->value) + { + dmPrint(0, "%08x : %s match %d / 0x%x\n", + offs, def->name, mval, mval); + + } + } + } + } + goto out; + } + // Allocate comparision buffer // XXX: integer overflow? dmPrint(2, "Allocating %d element (%d bytes) comparision buffer.\n",