Mercurial > hg > sidinfo
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 |