changeset 59:718cdb563531

Initial implementation of -F format string option.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 26 Dec 2015 22:58:18 +0200
parents 5ea07110f0cf
children accac31e1eda
files sidinfo.c
diffstat 1 files changed, 199 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- 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 <field separator>", 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))