# HG changeset patch # User Matti Hamalainen # Date 1451163498 -7200 # Node ID 718cdb56353160ff7916c4d4f25480b7ef458576 # Parent 5ea07110f0cfce733767626eec772acf006cd123 Initial implementation of -F format string option. diff -r 5ea07110f0cf -r 718cdb563531 sidinfo.c --- a/sidinfo.c Sat Dec 26 22:57:23 2015 +0200 +++ b/sidinfo.c Sat Dec 26 22:58:18 2015 +0200 @@ -80,6 +80,21 @@ static const int noptPSFlags = sizeof(optPSFlags) / sizeof(optPSFlags[0]); +typedef struct +{ + int cmd; + char *str; + char chr; +} PSFStackItem; + + +typedef struct +{ + int nitems, nallocated; + PSFStackItem *items; +} PSFStack; + + // Option variables BOOL optParsable = FALSE, optNoNamePrefix = FALSE, @@ -89,6 +104,8 @@ uint32_t optFields = SIF_ALL; int optNFiles = 0; +PSFStack optFormat; + // Define option arguments static const th_optarg_t optList[] = @@ -100,6 +117,7 @@ { 6, 'l', "line", "Output in one line format, -l ", OPT_ARGREQ }, { 3, 'f', "fields", "Show only specified field(s)", OPT_ARGREQ }, { 4, 'x', "hex", "Use hexadecimal values", OPT_NONE }, + { 7, 'F', "format", "Use given format string (see below)", OPT_ARGREQ }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); @@ -127,13 +145,18 @@ } printf("Example: %s -x -p -f hash,copyright somesidfile.sid\n", th_prog_name); + + printf( + "\nFormat strings for '-F' option are composed of @fields@ that\n" + "are expanded to their value. Also, escape sequences like \\r,\\n, etc.\n" + "can be used: -F 'hash=@hash@\\ncopy=@copyright@\\n'\n" + ); } -int argMatchPSField(const char *field) +int argMatchPSField(const char *field, const size_t len) { int index, found = -1; - size_t len = strlen(field); for (index = 0; index < noptPSFlags; index++) { const PSFOption *opt = &optPSFlags[index]; @@ -153,15 +176,12 @@ { // Trim whitespace if (end != NULL) - { - *end = 0; - for (end--; end > opt && *end && th_isspace(*end); end--) - *end = 0; - } + while (end > opt && *end && th_isspace(*end)) end--; + while (*opt && th_isspace(*opt)) opt++; // Match field name - int found = argMatchPSField(opt); + int found = argMatchPSField(opt, (end != NULL) ? end - opt : strlen(opt)); switch (found) { case -1: @@ -179,6 +199,121 @@ } +BOOL siStackAddItem(PSFStack *stack, const PSFStackItem *item) +{ + if (stack->items == NULL || stack->nitems + 1 >= stack->nallocated) + { + stack->nallocated += 16; + if ((stack->items = th_realloc(stack->items, stack->nallocated * sizeof(PSFStackItem))) == NULL) + { + THERR("Could not allocate memory for format item stack.\n"); + return FALSE; + } + } + + memcpy(stack->items + stack->nitems, item, sizeof(PSFStackItem)); + stack->nitems++; + return TRUE; +} + + +void siClearStack(PSFStack *stack) +{ + if (stack != NULL) + { + if (stack->nitems > 0 && stack->items != NULL) + { + int n; + for (n = 0; n < stack->nitems; n++) + { + if (stack->items[n].cmd == -1) + th_free(stack->items[n].str); + } + th_free(stack->items); + } + memset(stack, 0, sizeof(PSFStack)); + } +} + + +BOOL argParsePSFormatStr(const char *fmt) +{ + PSFStackItem item; + const char *start; + int mode = 0; + + siClearStack(&optFormat); + + while (mode != -1) + switch (mode) + { + case 0: + if (*fmt == '@') + { + start = fmt + 1; + mode = 1; + } + else + { + start = fmt; + mode = 2; + } + fmt++; + break; + + case 1: + if (*fmt == '@') + { + if (fmt - start == 0) + { + item.cmd = -2; + item.str = NULL; + item.chr = '@'; + if (!siStackAddItem(&optFormat, &item)) + return FALSE; + } + else + { + int ret = argMatchPSField(start, fmt - start); + if (ret >= 0) + { + item.cmd = ret; + item.str = NULL; + if (!siStackAddItem(&optFormat, &item)) + return FALSE; + } + else + { + THERR("Foobar %d\n", ret); + return FALSE; + } + } + mode = 0; + } + else + if (*fmt == 0) + mode = -1; + fmt++; + break; + + case 2: + if (*fmt == 0 || *fmt == '@') + { + item.cmd = -1; + item.str = th_strndup(start, fmt - start); + if (!siStackAddItem(&optFormat, &item)) + return FALSE; + mode = (*fmt == 0) ? -1 : 0; + } + else + fmt++; + break; + } + + return TRUE; +} + + BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) @@ -229,6 +364,11 @@ optFieldSep = optArg; break; + case 7: + if (!argParsePSFormatStr(optArg)) + return FALSE; + break; + default: THERR("Unknown option '%s'.\n", currArg); return FALSE; @@ -467,21 +607,22 @@ static void siPrintFieldPrefix(FILE *outFile, const char *name) { - if (!optNoNamePrefix) + if (!optNoNamePrefix && !optFormat.nitems) fprintf(outFile, optParsable ? "%s=" : "%-20s : ", name); } static void siPrintFieldSeparator(FILE *outFile) { - fprintf(outFile, optOneLine ? optFieldSep : "\n"); + if (!optFormat.nitems) + fprintf(outFile, optOneLine ? optFieldSep : "\n"); } static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const int xindex, const char *xfmt, const char *xaltfmt, ...) { const PSFOption *opt = &optPSFlags[xindex]; - if (optFields & opt->flag) + if (optFormat.nitems || (optFields & opt->flag)) { va_list ap; const char *fmt = optHexadecimal ? (xaltfmt != NULL ? xaltfmt : xfmt) : xfmt; @@ -499,7 +640,8 @@ #define PR(xfmt, xaltfmt, ...) siPrintPSIDInfoLine(outFile, shown, xindex, xfmt, xaltfmt, __VA_ARGS__ ) -void siPrintPSIDInformationField(FILE *outFile, const char *filename, const PSIDHeader *psid, BOOL *shown, const int xindex) + +static void siPrintPSIDInformationField(FILE *outFile, const char *filename, const PSIDHeader *psid, BOOL *shown, const int xindex) { switch (xindex) { @@ -534,7 +676,7 @@ case 17: { const PSFOption *opt = &optPSFlags[xindex]; - if (optFormatStack || (optFields & opt->flag)) + if (optFormat.nitems || (optFields & opt->flag)) { siPrintFieldPrefix(outFile, "Hash"); th_md5_print(outFile, psid->hash); @@ -570,9 +712,51 @@ } // Output + if (optFormat.nitems) + { + for (index = 0; index < optFormat.nitems; index++) + { + PSFStackItem *item = &optFormat.items[index]; + switch (item->cmd) + { + case -1: + { + char *str = item->str; + while (*str) + { + if (*str == '\\') + { + str++; + switch (*str) + { + case 'n': fputc('\n', outFile); break; + case 'r': fputc('\r', outFile); break; + case 't': fputc('\r', outFile); break; + case '\\': fputc('\\', outFile); break; + } + } + else + fputc(*str, outFile); + + str++; + } + } + break; + + case -2: + fputc(item->chr, outFile); + break; + + default: + siPrintPSIDInformationField(outFile, filename, &psid, &shown, item->cmd); + } + } + } + else { for (index = 0; index < noptPSFlags - 1; index++) siPrintPSIDInformationField(outFile, filename, &psid, &shown, index); + if (shown && optOneLine) fprintf(outFile, "\n"); } @@ -592,6 +776,8 @@ th_init("SIDInfo", "PSID/RSID information displayer", "0.5.3", NULL, NULL); th_verbosityLevel = 0; + memset(&optFormat, 0, sizeof(optFormat)); + // Parse command line arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_ONLY_OPTS))