comparison sidinfo.c @ 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
comparison
equal deleted inserted replaced
58:5ea07110f0cf 59:718cdb563531
78 }; 78 };
79 79
80 static const int noptPSFlags = sizeof(optPSFlags) / sizeof(optPSFlags[0]); 80 static const int noptPSFlags = sizeof(optPSFlags) / sizeof(optPSFlags[0]);
81 81
82 82
83 typedef struct
84 {
85 int cmd;
86 char *str;
87 char chr;
88 } PSFStackItem;
89
90
91 typedef struct
92 {
93 int nitems, nallocated;
94 PSFStackItem *items;
95 } PSFStack;
96
97
83 // Option variables 98 // Option variables
84 BOOL optParsable = FALSE, 99 BOOL optParsable = FALSE,
85 optNoNamePrefix = FALSE, 100 optNoNamePrefix = FALSE,
86 optHexadecimal = FALSE, 101 optHexadecimal = FALSE,
87 optOneLine = FALSE; 102 optOneLine = FALSE;
88 char *optFieldSep = NULL; 103 char *optFieldSep = NULL;
89 uint32_t optFields = SIF_ALL; 104 uint32_t optFields = SIF_ALL;
90 int optNFiles = 0; 105 int optNFiles = 0;
91 106
107 PSFStack optFormat;
108
92 109
93 // Define option arguments 110 // Define option arguments
94 static const th_optarg_t optList[] = 111 static const th_optarg_t optList[] =
95 { 112 {
96 { 0, '?', "help", "Show this help", OPT_NONE }, 113 { 0, '?', "help", "Show this help", OPT_NONE },
98 { 2, 'p', "parsable", "Output in script-parsable format", OPT_NONE }, 115 { 2, 'p', "parsable", "Output in script-parsable format", OPT_NONE },
99 { 5, 'n', "noprefix", "Output without field name prefix", OPT_NONE }, 116 { 5, 'n', "noprefix", "Output without field name prefix", OPT_NONE },
100 { 6, 'l', "line", "Output in one line format, -l <field separator>", OPT_ARGREQ }, 117 { 6, 'l', "line", "Output in one line format, -l <field separator>", OPT_ARGREQ },
101 { 3, 'f', "fields", "Show only specified field(s)", OPT_ARGREQ }, 118 { 3, 'f', "fields", "Show only specified field(s)", OPT_ARGREQ },
102 { 4, 'x', "hex", "Use hexadecimal values", OPT_NONE }, 119 { 4, 'x', "hex", "Use hexadecimal values", OPT_NONE },
120 { 7, 'F', "format", "Use given format string (see below)", OPT_ARGREQ },
103 }; 121 };
104 122
105 static const int optListN = sizeof(optList) / sizeof(optList[0]); 123 static const int optListN = sizeof(optList) / sizeof(optList[0]);
106 124
107 125
125 n = 0; 143 n = 0;
126 } 144 }
127 } 145 }
128 146
129 printf("Example: %s -x -p -f hash,copyright somesidfile.sid\n", th_prog_name); 147 printf("Example: %s -x -p -f hash,copyright somesidfile.sid\n", th_prog_name);
130 } 148
131 149 printf(
132 150 "\nFormat strings for '-F' option are composed of @fields@ that\n"
133 int argMatchPSField(const char *field) 151 "are expanded to their value. Also, escape sequences like \\r,\\n, etc.\n"
152 "can be used: -F 'hash=@hash@\\ncopy=@copyright@\\n'\n"
153 );
154 }
155
156
157 int argMatchPSField(const char *field, const size_t len)
134 { 158 {
135 int index, found = -1; 159 int index, found = -1;
136 size_t len = strlen(field);
137 for (index = 0; index < noptPSFlags; index++) 160 for (index = 0; index < noptPSFlags; index++)
138 { 161 {
139 const PSFOption *opt = &optPSFlags[index]; 162 const PSFOption *opt = &optPSFlags[index];
140 if (th_strncasecmp(opt->name, field, len) == 0) 163 if (th_strncasecmp(opt->name, field, len) == 0)
141 { 164 {
151 174
152 BOOL argParsePSField(char *opt, char *end, uint32_t *fields) 175 BOOL argParsePSField(char *opt, char *end, uint32_t *fields)
153 { 176 {
154 // Trim whitespace 177 // Trim whitespace
155 if (end != NULL) 178 if (end != NULL)
156 { 179 while (end > opt && *end && th_isspace(*end)) end--;
157 *end = 0; 180
158 for (end--; end > opt && *end && th_isspace(*end); end--)
159 *end = 0;
160 }
161 while (*opt && th_isspace(*opt)) opt++; 181 while (*opt && th_isspace(*opt)) opt++;
162 182
163 // Match field name 183 // Match field name
164 int found = argMatchPSField(opt); 184 int found = argMatchPSField(opt, (end != NULL) ? end - opt : strlen(opt));
165 switch (found) 185 switch (found)
166 { 186 {
167 case -1: 187 case -1:
168 THERR("No such field '%s'.\n", opt); 188 THERR("No such field '%s'.\n", opt);
169 return FALSE; 189 return FALSE;
174 194
175 default: 195 default:
176 *fields |= optPSFlags[found].flag; 196 *fields |= optPSFlags[found].flag;
177 return TRUE; 197 return TRUE;
178 } 198 }
199 }
200
201
202 BOOL siStackAddItem(PSFStack *stack, const PSFStackItem *item)
203 {
204 if (stack->items == NULL || stack->nitems + 1 >= stack->nallocated)
205 {
206 stack->nallocated += 16;
207 if ((stack->items = th_realloc(stack->items, stack->nallocated * sizeof(PSFStackItem))) == NULL)
208 {
209 THERR("Could not allocate memory for format item stack.\n");
210 return FALSE;
211 }
212 }
213
214 memcpy(stack->items + stack->nitems, item, sizeof(PSFStackItem));
215 stack->nitems++;
216 return TRUE;
217 }
218
219
220 void siClearStack(PSFStack *stack)
221 {
222 if (stack != NULL)
223 {
224 if (stack->nitems > 0 && stack->items != NULL)
225 {
226 int n;
227 for (n = 0; n < stack->nitems; n++)
228 {
229 if (stack->items[n].cmd == -1)
230 th_free(stack->items[n].str);
231 }
232 th_free(stack->items);
233 }
234 memset(stack, 0, sizeof(PSFStack));
235 }
236 }
237
238
239 BOOL argParsePSFormatStr(const char *fmt)
240 {
241 PSFStackItem item;
242 const char *start;
243 int mode = 0;
244
245 siClearStack(&optFormat);
246
247 while (mode != -1)
248 switch (mode)
249 {
250 case 0:
251 if (*fmt == '@')
252 {
253 start = fmt + 1;
254 mode = 1;
255 }
256 else
257 {
258 start = fmt;
259 mode = 2;
260 }
261 fmt++;
262 break;
263
264 case 1:
265 if (*fmt == '@')
266 {
267 if (fmt - start == 0)
268 {
269 item.cmd = -2;
270 item.str = NULL;
271 item.chr = '@';
272 if (!siStackAddItem(&optFormat, &item))
273 return FALSE;
274 }
275 else
276 {
277 int ret = argMatchPSField(start, fmt - start);
278 if (ret >= 0)
279 {
280 item.cmd = ret;
281 item.str = NULL;
282 if (!siStackAddItem(&optFormat, &item))
283 return FALSE;
284 }
285 else
286 {
287 THERR("Foobar %d\n", ret);
288 return FALSE;
289 }
290 }
291 mode = 0;
292 }
293 else
294 if (*fmt == 0)
295 mode = -1;
296 fmt++;
297 break;
298
299 case 2:
300 if (*fmt == 0 || *fmt == '@')
301 {
302 item.cmd = -1;
303 item.str = th_strndup(start, fmt - start);
304 if (!siStackAddItem(&optFormat, &item))
305 return FALSE;
306 mode = (*fmt == 0) ? -1 : 0;
307 }
308 else
309 fmt++;
310 break;
311 }
312
313 return TRUE;
179 } 314 }
180 315
181 316
182 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) 317 BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
183 { 318 {
225 break; 360 break;
226 361
227 case 6: 362 case 6:
228 optOneLine = TRUE; 363 optOneLine = TRUE;
229 optFieldSep = optArg; 364 optFieldSep = optArg;
365 break;
366
367 case 7:
368 if (!argParsePSFormatStr(optArg))
369 return FALSE;
230 break; 370 break;
231 371
232 default: 372 default:
233 THERR("Unknown option '%s'.\n", currArg); 373 THERR("Unknown option '%s'.\n", currArg);
234 return FALSE; 374 return FALSE;
465 } 605 }
466 606
467 607
468 static void siPrintFieldPrefix(FILE *outFile, const char *name) 608 static void siPrintFieldPrefix(FILE *outFile, const char *name)
469 { 609 {
470 if (!optNoNamePrefix) 610 if (!optNoNamePrefix && !optFormat.nitems)
471 fprintf(outFile, optParsable ? "%s=" : "%-20s : ", name); 611 fprintf(outFile, optParsable ? "%s=" : "%-20s : ", name);
472 } 612 }
473 613
474 614
475 static void siPrintFieldSeparator(FILE *outFile) 615 static void siPrintFieldSeparator(FILE *outFile)
476 { 616 {
477 fprintf(outFile, optOneLine ? optFieldSep : "\n"); 617 if (!optFormat.nitems)
618 fprintf(outFile, optOneLine ? optFieldSep : "\n");
478 } 619 }
479 620
480 621
481 static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const int xindex, const char *xfmt, const char *xaltfmt, ...) 622 static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const int xindex, const char *xfmt, const char *xaltfmt, ...)
482 { 623 {
483 const PSFOption *opt = &optPSFlags[xindex]; 624 const PSFOption *opt = &optPSFlags[xindex];
484 if (optFields & opt->flag) 625 if (optFormat.nitems || (optFields & opt->flag))
485 { 626 {
486 va_list ap; 627 va_list ap;
487 const char *fmt = optHexadecimal ? (xaltfmt != NULL ? xaltfmt : xfmt) : xfmt; 628 const char *fmt = optHexadecimal ? (xaltfmt != NULL ? xaltfmt : xfmt) : xfmt;
488 629
489 siPrintFieldPrefix(outFile, (optParsable || opt->lname == NULL) ? opt->name : opt->lname); 630 siPrintFieldPrefix(outFile, (optParsable || opt->lname == NULL) ? opt->name : opt->lname);
497 } 638 }
498 } 639 }
499 640
500 #define PR(xfmt, xaltfmt, ...) siPrintPSIDInfoLine(outFile, shown, xindex, xfmt, xaltfmt, __VA_ARGS__ ) 641 #define PR(xfmt, xaltfmt, ...) siPrintPSIDInfoLine(outFile, shown, xindex, xfmt, xaltfmt, __VA_ARGS__ )
501 642
502 void siPrintPSIDInformationField(FILE *outFile, const char *filename, const PSIDHeader *psid, BOOL *shown, const int xindex) 643
644 static void siPrintPSIDInformationField(FILE *outFile, const char *filename, const PSIDHeader *psid, BOOL *shown, const int xindex)
503 { 645 {
504 switch (xindex) 646 switch (xindex)
505 { 647 {
506 case 0: PR("%s", NULL, filename); break; 648 case 0: PR("%s", NULL, filename); break;
507 case 1: PR("%s", NULL, psid->magic); break; 649 case 1: PR("%s", NULL, psid->magic); break;
532 case 16: PR("%s", NULL, psid->sidCopyright); break; 674 case 16: PR("%s", NULL, psid->sidCopyright); break;
533 675
534 case 17: 676 case 17:
535 { 677 {
536 const PSFOption *opt = &optPSFlags[xindex]; 678 const PSFOption *opt = &optPSFlags[xindex];
537 if (optFormatStack || (optFields & opt->flag)) 679 if (optFormat.nitems || (optFields & opt->flag))
538 { 680 {
539 siPrintFieldPrefix(outFile, "Hash"); 681 siPrintFieldPrefix(outFile, "Hash");
540 th_md5_print(outFile, psid->hash); 682 th_md5_print(outFile, psid->hash);
541 siPrintFieldSeparator(outFile); 683 siPrintFieldSeparator(outFile);
542 } 684 }
568 THERR("Error reading %s\n", filename); 710 THERR("Error reading %s\n", filename);
569 goto error; 711 goto error;
570 } 712 }
571 713
572 // Output 714 // Output
715 if (optFormat.nitems)
716 {
717 for (index = 0; index < optFormat.nitems; index++)
718 {
719 PSFStackItem *item = &optFormat.items[index];
720 switch (item->cmd)
721 {
722 case -1:
723 {
724 char *str = item->str;
725 while (*str)
726 {
727 if (*str == '\\')
728 {
729 str++;
730 switch (*str)
731 {
732 case 'n': fputc('\n', outFile); break;
733 case 'r': fputc('\r', outFile); break;
734 case 't': fputc('\r', outFile); break;
735 case '\\': fputc('\\', outFile); break;
736 }
737 }
738 else
739 fputc(*str, outFile);
740
741 str++;
742 }
743 }
744 break;
745
746 case -2:
747 fputc(item->chr, outFile);
748 break;
749
750 default:
751 siPrintPSIDInformationField(outFile, filename, &psid, &shown, item->cmd);
752 }
753 }
754 }
755 else
573 { 756 {
574 for (index = 0; index < noptPSFlags - 1; index++) 757 for (index = 0; index < noptPSFlags - 1; index++)
575 siPrintPSIDInformationField(outFile, filename, &psid, &shown, index); 758 siPrintPSIDInformationField(outFile, filename, &psid, &shown, index);
759
576 if (shown && optOneLine) 760 if (shown && optOneLine)
577 fprintf(outFile, "\n"); 761 fprintf(outFile, "\n");
578 } 762 }
579 763
580 // Shutdown 764 // Shutdown
590 { 774 {
591 // Initialize 775 // Initialize
592 th_init("SIDInfo", "PSID/RSID information displayer", "0.5.3", NULL, NULL); 776 th_init("SIDInfo", "PSID/RSID information displayer", "0.5.3", NULL, NULL);
593 th_verbosityLevel = 0; 777 th_verbosityLevel = 0;
594 778
779 memset(&optFormat, 0, sizeof(optFormat));
780
595 // Parse command line arguments 781 // Parse command line arguments
596 if (!th_args_process(argc, argv, optList, optListN, 782 if (!th_args_process(argc, argv, optList, optListN,
597 argHandleOpt, argHandleFile, OPTH_ONLY_OPTS)) 783 argHandleOpt, argHandleFile, OPTH_ONLY_OPTS))
598 return -1; 784 return -1;
599 785