Mercurial > hg > sidinfo
annotate sidinfo.c @ 34:50fe65830c03
Fix non-one-line entry output.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 26 Sep 2014 01:54:34 +0300 |
parents | 39d9df17c8b1 |
children | 79e6d08b473f |
rev | line source |
---|---|
2 | 1 /* |
2 * SIDInfo - PSID/RSID information displayer | |
3 * Written by Matti 'ccr' Hämäläinen | |
4 * (C) Copyright 2014 Tecnic Software productions (TNSP) | |
5 */ | |
0 | 6 #include "th_args.h" |
7 #include "th_endian.h" | |
2 | 8 #include "th_string.h" |
0 | 9 #include "th_crypto.h" |
10 | |
28 | 11 // Some constants |
2 | 12 #define PSID_MAGIC_LEN 4 |
13 #define PSID_STR_LEN 32 | |
1 | 14 #define PSID_BUFFER_SIZE (1024 * 16) |
15 | |
0 | 16 |
28 | 17 // Flags for various information fields |
2 | 18 enum |
19 { | |
32 | 20 PSF_NONE = 0, |
2 | 21 |
32 | 22 PSF_TYPE = 0x00000001, |
2 | 23 PSF_VERSION = 0x00000002, |
24 PSF_DATA_OFFS = 0x00000004, | |
25 PSF_LOAD_ADDR = 0x00000008, | |
26 PSF_INIT_ADDR = 0x00000010, | |
27 PSF_PLAY_ADDR = 0x00000020, | |
28 PSF_SONGS = 0x00000040, | |
29 PSF_START_SONG = 0x00000080, | |
30 PSF_SPEEDS = 0x00000100, | |
31 PSF_SID_NAME = 0x00000200, | |
32 PSF_SID_AUTHOR = 0x00000400, | |
33 PSF_SID_COPYRIGHT = 0x00000800, | |
34 | |
35 PSF_DATA_SIZE = 0x00100000, | |
36 PSF_HASH = 0x00200000, | |
9
c1fba4abf56f
Make filename printing optional.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
37 PSF_FILENAME = 0x10000000, |
2 | 38 |
39 PSF_ALL = 0xffffffff, | |
40 }; | |
41 | |
42 | |
0 | 43 typedef struct |
44 { | |
2 | 45 uint32_t flag; |
46 char *name; | |
47 char *lname; | |
48 } PSFOption; | |
49 | |
50 | |
51 const PSFOption optPSFlags[] = | |
52 { | |
53 { PSF_TYPE , "Type" , NULL }, | |
54 { PSF_VERSION , "Version" , NULL }, | |
55 { PSF_DATA_OFFS , "DataOffs" , "Data offset" }, | |
56 { PSF_DATA_SIZE , "DataSize" , "Data size" }, | |
57 { PSF_LOAD_ADDR , "LoadAddr" , "Load address" }, | |
58 { PSF_INIT_ADDR , "InitAddr" , "Init address" }, | |
59 { PSF_PLAY_ADDR , "PlayAddr" , "Play address" }, | |
60 { PSF_SONGS , "Songs" , "Songs" }, | |
61 { PSF_START_SONG , "StartSong" , "Start song" }, | |
62 { PSF_SID_NAME , "Name" , NULL }, | |
63 { PSF_SID_AUTHOR , "Author" , NULL }, | |
64 { PSF_SID_COPYRIGHT , "Copyright" , NULL }, | |
65 { PSF_HASH , "Hash" , NULL }, | |
9
c1fba4abf56f
Make filename printing optional.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
66 { PSF_FILENAME , "Filename" , NULL }, |
28 | 67 |
68 | |
2 | 69 { PSF_ALL , "All" , NULL }, |
70 }; | |
71 | |
72 const int noptPSFlags = sizeof(optPSFlags) / sizeof(optPSFlags[0]); | |
73 | |
74 | |
28 | 75 // Option variables |
2 | 76 BOOL optParsable = FALSE, |
7 | 77 optNoNamePrefix = FALSE, |
29 | 78 optHexadecimal = FALSE, |
79 optOneLine = FALSE; | |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
80 char * optFieldSep = NULL; |
2 | 81 uint32_t optFields = PSF_ALL; |
19 | 82 int optNFiles = 0; |
2 | 83 |
84 | |
28 | 85 // Define option arguments |
2 | 86 static optarg_t optList[] = |
87 { | |
88 { 0, '?', "help", "Show this help", OPT_NONE }, | |
24
16d6b5029543
Disable verbose option for now, it does not do anything at the moment.
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
89 // { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, |
2 | 90 { 2, 'p', "parsable", "Output in script-parsable format", OPT_NONE }, |
7 | 91 { 5, 'n', "noprefix", "Output without field name prefix", OPT_NONE }, |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
92 { 6, 'l', "line", "Output in one line format, -l <field separator>", OPT_ARGREQ }, |
2 | 93 { 3, 'f', "fields", "Show only specified field(s)", OPT_ARGREQ }, |
94 { 4, 'x', "hex", "Use hexadecimal values", OPT_NONE }, | |
95 }; | |
96 | |
97 static const int optListN = (sizeof(optList) / sizeof(optList[0])); | |
98 | |
99 | |
100 void argShowHelp(void) | |
101 { | |
102 int index, n; | |
103 | |
20 | 104 th_print_banner(stdout, th_prog_name, "[options] <sid filename> [sid filename #2 ..]"); |
2 | 105 th_args_help(stdout, optList, optListN); |
106 printf( | |
107 "\n" | |
108 "Available fields:\n"); | |
3 | 109 |
2 | 110 for (index = n = 0; index < noptPSFlags; index++) |
111 { | |
112 const PSFOption *opt = &optPSFlags[index]; | |
113 printf("%s%s", opt->name, (index < noptPSFlags - 1) ? ", " : "\n\n"); | |
114 if (++n > 5) | |
115 { | |
116 printf("\n"); | |
117 n = 0; | |
118 } | |
119 } | |
5 | 120 |
121 printf("Example: %s -x -p -f hash,copyright somesidfile.sid\n", th_prog_name); | |
2 | 122 } |
123 | |
124 | |
125 int argMatchPSField(const char *field) | |
126 { | |
127 int index, found = -1; | |
128 size_t len = strlen(field); | |
129 for (index = 0; index < noptPSFlags; index++) | |
130 { | |
131 const PSFOption *opt = &optPSFlags[index]; | |
132 if (strncasecmp(opt->name, field, len) == 0) | |
133 { | |
134 if (found >= 0) | |
135 return -2; | |
136 found = index; | |
137 } | |
138 } | |
139 | |
140 return found; | |
141 } | |
142 | |
143 | |
144 BOOL argParsePSField(char *opt, char *end, uint32_t *fields) | |
145 { | |
146 // Trim whitespace | |
147 if (end != NULL) | |
148 { | |
149 *end = 0; | |
150 for (end--; end > opt && *end && th_isspace(*end); end--) | |
151 *end = 0; | |
152 } | |
153 while (*opt && th_isspace(*opt)) opt++; | |
154 | |
155 // Match field name | |
156 int found = argMatchPSField(opt); | |
157 switch (found) | |
158 { | |
159 case -1: | |
160 THERR("No such flag '%s'.\n", opt); | |
161 return FALSE; | |
162 | |
163 case -2: | |
164 THERR("Flag '%s' is ambiguous.\n", opt); | |
165 return FALSE; | |
3 | 166 |
2 | 167 default: |
168 *fields |= optPSFlags[found].flag; | |
169 return TRUE; | |
170 } | |
171 } | |
172 | |
173 | |
174 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
175 { | |
176 switch (optN) | |
177 { | |
178 case 0: | |
179 argShowHelp(); | |
180 exit(0); | |
181 break; | |
182 | |
183 case 1: | |
184 th_verbosityLevel++; | |
185 break; | |
186 | |
187 case 2: | |
188 optParsable = TRUE; | |
189 break; | |
190 | |
191 case 3: | |
192 { | |
193 char *start = optArg; | |
194 optFields = PSF_NONE; | |
195 | |
196 while (*start) | |
197 { | |
198 char *end = strchr(start, ','); | |
199 | |
200 if (!argParsePSField(start, end, &optFields)) | |
201 return FALSE; | |
202 | |
203 if (!end) | |
204 break; | |
205 | |
206 start = end + 1; | |
207 } | |
3 | 208 |
2 | 209 //fprintf(stderr, "%08x\n", optFields); |
210 } | |
211 break; | |
212 | |
213 case 4: | |
214 optHexadecimal = TRUE; | |
215 break; | |
216 | |
7 | 217 case 5: |
218 optNoNamePrefix = TRUE; | |
219 break; | |
220 | |
29 | 221 case 6: |
222 optOneLine = TRUE; | |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
223 optFieldSep = optArg; |
29 | 224 break; |
225 | |
2 | 226 default: |
227 THERR("Unknown option '%s'.\n", currArg); | |
228 return FALSE; | |
229 } | |
230 | |
231 return TRUE; | |
232 } | |
233 | |
234 | |
235 typedef struct | |
236 { | |
237 char magic[PSID_MAGIC_LEN + 1]; // "PSID" / "RSID" magic identifier | |
238 uint16_t | |
239 version, // Version number | |
0 | 240 dataOffset, // Start of actual c64 data in file |
241 loadAddress, // Loading address | |
242 initAddress, // Initialization address | |
243 playAddress, // Play one frame | |
244 nSongs, // Number of subsongs | |
245 startSong; // Default starting song | |
246 uint32_t speed; // Speed | |
2 | 247 char sidName[PSID_STR_LEN + 1]; // Descriptive text-fields, ASCIIZ |
248 char sidAuthor[PSID_STR_LEN + 1]; | |
249 char sidCopyright[PSID_STR_LEN + 1]; | |
0 | 250 |
251 // PSIDv2 data | |
252 uint16_t flags; // Flags | |
253 uint8_t startPage, pageLength; | |
254 uint16_t reserved; | |
255 | |
1 | 256 // Extra data |
0 | 257 BOOL isRSID; |
1 | 258 size_t dataSize; // Total size of data - header |
259 th_md5hash_t hash; // Songlength database hash | |
0 | 260 |
261 } PSIDHeader; | |
262 | |
263 | |
2 | 264 static void siAppendHash16(th_md5state_t *state, uint16_t data) |
265 { | |
266 uint8_t ib8[2]; | |
267 ib8[0] = data & 0xff; | |
268 ib8[1] = data >> 8; | |
269 th_md5_append(state, (uint8_t *) &ib8, sizeof(ib8)); | |
270 } | |
271 | |
272 | |
273 int siReadPSIDFile(FILE *inFile, PSIDHeader *psid) | |
0 | 274 { |
1 | 275 th_md5state_t state; |
276 uint8_t tmp8, *fileData = NULL; | |
277 int index, ret = -1; | |
278 size_t read; | |
279 BOOL first; | |
280 | |
281 memset(psid, 0, sizeof(*psid)); | |
282 | |
283 if ((fileData = (uint8_t *) th_malloc(PSID_BUFFER_SIZE)) == NULL) | |
284 { | |
285 THERR("Error allocating temporary data buffer of %d bytes.\n", PSID_BUFFER_SIZE); | |
286 goto error; | |
287 } | |
3 | 288 |
0 | 289 // Read PSID header in |
2 | 290 if (!th_fread_str(inFile, (uint8_t *) psid->magic, PSID_MAGIC_LEN) || |
1 | 291 !th_fread_be16(inFile, &psid->version) || |
292 !th_fread_be16(inFile, &psid->dataOffset) || | |
293 !th_fread_be16(inFile, &psid->loadAddress) || | |
294 !th_fread_be16(inFile, &psid->initAddress) || | |
295 !th_fread_be16(inFile, &psid->playAddress) || | |
296 !th_fread_be16(inFile, &psid->nSongs) || | |
297 !th_fread_be16(inFile, &psid->startSong) || | |
298 !th_fread_be32(inFile, &psid->speed)) | |
0 | 299 { |
1 | 300 THERR("Could not read PSID/RSID header.\n"); |
0 | 301 goto error; |
302 } | |
303 | |
2 | 304 psid->magic[PSID_MAGIC_LEN] = 0; |
305 | |
0 | 306 if ((psid->magic[0] != 'R' && psid->magic[0] != 'P') || |
307 psid->magic[1] != 'S' || psid->magic[2] != 'I' || psid->magic[3] != 'D' || | |
308 psid->version < 1 || psid->version > 3) | |
309 { | |
1 | 310 THERR("Not a supported PSID or RSID file.\n"); |
0 | 311 goto error; |
312 } | |
313 | |
1 | 314 psid->isRSID = psid->magic[0] == 'R'; |
0 | 315 |
2 | 316 if (!th_fread_str(inFile, (uint8_t *)psid->sidName, PSID_STR_LEN) || |
317 !th_fread_str(inFile, (uint8_t *)psid->sidAuthor, PSID_STR_LEN) || | |
318 !th_fread_str(inFile, (uint8_t *)psid->sidCopyright, PSID_STR_LEN)) | |
0 | 319 { |
1 | 320 THERR("Error reading SID file header.\n"); |
0 | 321 goto error; |
322 } | |
323 | |
2 | 324 psid->sidName[PSID_STR_LEN] = 0; |
325 psid->sidAuthor[PSID_STR_LEN] = 0; | |
326 psid->sidCopyright[PSID_STR_LEN] = 0; | |
327 | |
0 | 328 // Check if we need to load PSIDv2NG header ... |
329 if (psid->version >= 2) | |
330 { | |
331 // Yes, we need to | |
1 | 332 if (!th_fread_be16(inFile, &psid->flags) || |
333 !th_fread_byte(inFile, &psid->startPage) || | |
334 !th_fread_byte(inFile, &psid->pageLength) || | |
335 !th_fread_be16(inFile, &psid->reserved)) | |
0 | 336 { |
1 | 337 THERR("Error reading PSID/RSID v2+ extra header data.\n"); |
0 | 338 goto error; |
339 } | |
340 } | |
341 | |
1 | 342 // Initialize MD5-hash calculation |
343 th_md5_init(&state); | |
0 | 344 |
1 | 345 // Process actual data |
346 psid->dataSize = 0; | |
347 first = TRUE; | |
2 | 348 do |
349 { | |
1 | 350 read = fread(fileData, sizeof(uint8_t), PSID_BUFFER_SIZE, inFile); |
351 psid->dataSize += read; | |
0 | 352 |
1 | 353 if (first && psid->loadAddress == 0) |
354 { | |
8
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
355 if (read < 4) |
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
356 { |
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
357 THERR("Error reading song data, unexpectedly small file.\n"); |
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
358 goto error; |
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
359 } |
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
360 |
1 | 361 // Strip load address (2 first bytes) |
362 th_md5_append(&state, &fileData[2], read - 2); | |
363 first = FALSE; | |
364 } | |
365 else | |
8
cfc74ec918dc
Fix handling of "large" SID files.
Matti Hamalainen <ccr@tnsp.org>
parents:
7
diff
changeset
|
366 if (read > 0) |
1 | 367 { |
368 // Append "as is" | |
369 th_md5_append(&state, fileData, read); | |
370 } | |
371 } while (read > 0 && !feof(inFile)); | |
0 | 372 |
1 | 373 // Append header data to hash |
2 | 374 siAppendHash16(&state, psid->initAddress); |
375 siAppendHash16(&state, psid->playAddress); | |
376 siAppendHash16(&state, psid->nSongs); | |
0 | 377 |
378 // Append song speed data to hash | |
1 | 379 tmp8 = psid->isRSID ? 60 : 0; |
0 | 380 for (index = 0; index < psid->nSongs && index < 32; index++) |
381 { | |
1 | 382 if (psid->isRSID) |
383 tmp8 = 60; | |
0 | 384 else |
1 | 385 tmp8 = (psid->speed & (1 << index)) ? 60 : 0; |
0 | 386 |
1 | 387 th_md5_append(&state, &tmp8, sizeof(tmp8)); |
0 | 388 } |
389 | |
390 // Rest of songs (more than 32) | |
391 for (index = 32; index < psid->nSongs; index++) | |
1 | 392 th_md5_append(&state, &tmp8, sizeof(tmp8)); |
0 | 393 |
394 // PSIDv2NG specific | |
395 if (psid->version >= 2) | |
396 { | |
397 // REFER TO SIDPLAY HEADERS FOR MORE INFORMATION | |
1 | 398 tmp8 = (psid->flags >> 2) & 3; |
399 if (tmp8 == 2) | |
400 th_md5_append(&state, &tmp8, sizeof(tmp8)); | |
0 | 401 } |
402 | |
403 // Calculate the hash | |
1 | 404 th_md5_finish(&state, psid->hash); |
405 ret = 0; | |
0 | 406 |
1 | 407 error: |
0 | 408 // Free buffer |
1 | 409 th_free(fileData); |
410 return ret; | |
0 | 411 } |
412 | |
413 | |
31 | 414 static void siPrintFieldPrefix(FILE *outFile, const char *name) |
2 | 415 { |
7 | 416 if (!optNoNamePrefix) |
29 | 417 fprintf(outFile, optParsable ? "%s=" : "%-20s : ", name); |
2 | 418 } |
419 | |
420 | |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
421 static void siPrintFieldSeparator(FILE *outFile) |
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
422 { |
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
423 fprintf(outFile, optOneLine ? optFieldSep : "\n"); |
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
424 } |
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
425 |
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
426 |
29 | 427 static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const int xindex, const char *xfmt, const char *xaltfmt, ...) |
2 | 428 { |
429 const PSFOption *opt = &optPSFlags[xindex]; | |
29 | 430 if (optFields & opt->flag) |
2 | 431 { |
432 va_list ap; | |
29 | 433 const char *fmt = optHexadecimal ? (xaltfmt != NULL ? xaltfmt : xfmt) : xfmt; |
2 | 434 |
31 | 435 siPrintFieldPrefix(outFile, (optParsable || opt->lname == NULL) ? opt->name : opt->lname); |
2 | 436 |
437 va_start(ap, xaltfmt); | |
438 | |
29 | 439 if (optParsable) |
2 | 440 vfprintf(outFile, fmt, ap); |
441 else | |
442 vfprintf(outFile, fmt, ap); | |
443 | |
444 va_end(ap); | |
3 | 445 |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
446 siPrintFieldSeparator(outFile); |
29 | 447 *shown = TRUE; |
2 | 448 } |
449 } | |
450 | |
29 | 451 #define PR(xindex, xfmt, xaltfmt, ...) siPrintPSIDInfoLine(outFile, &shown, xindex, xfmt, xaltfmt, __VA_ARGS__ ) |
2 | 452 |
453 | |
29 | 454 void siPrintPSIDInformation(FILE *outFile, const char *filename, const PSIDHeader *psid) |
2 | 455 { |
29 | 456 BOOL shown = FALSE; |
457 | |
16 | 458 PR(13, "%s", NULL, filename); |
9
c1fba4abf56f
Make filename printing optional.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
459 |
2 | 460 PR( 0, "%s", NULL, psid->magic); |
461 PR( 1, "%d.%d", NULL, (psid->version & 0xff), (psid->version >> 8)); | |
462 PR( 2, "%d", "$%08x", psid->dataOffset); | |
463 PR( 3, "%d", "$%08x", psid->dataSize); | |
464 PR( 4, "%d", "$%04x", psid->loadAddress); | |
465 PR( 5, "%d", "$%04x", psid->initAddress); | |
466 PR( 6, "%d", "$%04x", psid->playAddress); | |
467 PR( 7, "%d", "$%04x", psid->nSongs); | |
468 PR( 8, "%d", "$%04x", psid->startSong); | |
469 | |
470 PR( 9, "%s", NULL, psid->sidName); | |
471 PR(10, "%s", NULL, psid->sidAuthor); | |
3 | 472 PR(11, "%s", NULL, psid->sidCopyright); |
2 | 473 |
29 | 474 if (optFields & PSF_HASH) |
2 | 475 { |
31 | 476 siPrintFieldPrefix(outFile, "Hash"); |
2 | 477 th_md5_print(outFile, psid->hash); |
33
39d9df17c8b1
Add field separator argument to -l option.
Matti Hamalainen <ccr@tnsp.org>
parents:
32
diff
changeset
|
478 siPrintFieldSeparator(outFile); |
29 | 479 } |
480 | |
34
50fe65830c03
Fix non-one-line entry output.
Matti Hamalainen <ccr@tnsp.org>
parents:
33
diff
changeset
|
481 if (shown && optOneLine) |
2 | 482 fprintf(outFile, "\n"); |
483 } | |
484 | |
0 | 485 |
19 | 486 BOOL argHandleFile(char *filename) |
487 { | |
488 static PSIDHeader psid; | |
489 static FILE *inFile = NULL; | |
490 optNFiles++; | |
491 | |
492 if ((inFile = fopen(filename, "rb")) == NULL) | |
493 { | |
494 THERR("Could not open file '%s'.\n", filename); | |
27
4dd463eebb74
Fix argument handling when file does not exist.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
495 return TRUE; |
19 | 496 } |
497 | |
498 // Read PSID data | |
499 if (siReadPSIDFile(inFile, &psid) != 0) | |
23 | 500 { |
501 THERR("Error reading %s\n", filename); | |
19 | 502 goto error; |
23 | 503 } |
19 | 504 |
505 // Output | |
29 | 506 siPrintPSIDInformation(stdout, filename, &psid); |
19 | 507 |
508 // Shutdown | |
509 error: | |
510 if (inFile != NULL) | |
511 fclose(inFile); | |
512 | |
513 return TRUE; | |
514 } | |
515 | |
516 | |
0 | 517 int main(int argc, char *argv[]) |
518 { | |
519 // Initialize | |
25 | 520 th_init("SIDInfo", "PSID/RSID information displayer", "0.4", NULL, NULL); |
0 | 521 th_verbosityLevel = 0; |
522 | |
2 | 523 // Parse command line arguments |
524 if (!th_args_process(argc, argv, optList, optListN, | |
23 | 525 argHandleOpt, argHandleFile, OPTH_ONLY_OPTS)) |
2 | 526 return -1; |
527 | |
29 | 528 if (optOneLine) |
529 { | |
530 optParsable = FALSE; | |
531 optNoNamePrefix = TRUE; | |
532 } | |
533 | |
23 | 534 // Process files |
535 if (!th_args_process(argc, argv, optList, optListN, | |
536 argHandleOpt, argHandleFile, OPTH_ONLY_OTHER)) | |
537 return -2; | |
538 | |
19 | 539 if (optNFiles == 0) |
2 | 540 { |
541 argShowHelp(); | |
19 | 542 THERR("No filename(s) specified.\n"); |
2 | 543 } |
1 | 544 |
19 | 545 return 0; |
0 | 546 } |