1
/* ---------------------------------------------------------------------
2
* Implementation of Popt functions vk_popt.cpp
3
* This is a seriously hacked version of the popt libraries.
4
* No credit to me, all thanks and many apologies to the Red Hat team
5
* ---------------------------------------------------------------------
6
* popt is Copyright (c) 1998 Red Hat Software and distributed under
7
* an X11-style license, which is in turn compatible the GNU GPL v.2.
8
* See the file COPYING for the full license details.
12
#include "vk_option.h" /* namespace VkOPTION */
16
/* return a vkPoptOption initialised to null */
17
vkPoptOption nullOpt()
19
vkPoptOption _nullOpt = TABLE_END;
23
const char * vkPoptPeekArg( vkPoptContext con )
25
const char * ret = NULL;
26
if ( con && con->leftovers != NULL &&
27
con->nextLeftover < con->numLeftovers )
28
ret = con->leftovers[con->nextLeftover];
33
const char * vkPoptGetArg( vkPoptContext con )
35
const char * ret = NULL;
36
if ( con && con->leftovers != NULL &&
37
con->nextLeftover < con->numLeftovers ) {
38
ret = con->leftovers[con->nextLeftover++];
44
const char ** vkPoptGetArgs( vkPoptContext con )
46
if ( con == NULL || con->leftovers == NULL ||
47
con->numLeftovers == con->nextLeftover )
50
/* some apps like [like RPM ;-) ] need this NULL terminated */
51
con->leftovers[con->numLeftovers] = NULL;
52
return (con->leftovers + con->nextLeftover);
56
vkPoptContext vkPoptGetContext( int argc, const char ** argv,
57
const vkPoptOption * options )
59
vkPoptContext con = (vkPoptContext)malloc( sizeof(*con) );
65
memset( con, 0, sizeof(*con) );
67
con->os = con->optionStack;
70
con->os->next = 1; /* skip argv[0] */
71
con->leftovers = (const char**)calloc( (argc + 1), sizeof(*con->leftovers) );
72
con->options = options;
73
con->flags = PCONTEXT_POSIXMEHARDER;
74
con->finalArgvAlloced = argc * 2;
75
con->finalArgv = (const char**)calloc( con->finalArgvAlloced,
76
sizeof(*con->finalArgv) );
82
static void vkCleanOSE( struct optionStackEntry *os )
84
os->nextArg = (const char*)_free( os->nextArg );
85
os->argv = (const char**)_free( os->argv );
89
const vkPoptOption * vkFindOption( const vkPoptOption * opt,
90
const char * longFlag,
94
/* this happens when a single - is given */
95
if ( singleDash && !shortFlag && (longFlag && *longFlag == '\0') )
98
for (; opt->longFlag || opt->shortFlag || opt->arg; opt++) {
100
if ( opt->arg != NULL ) { /* is-a table */
101
/* recurse on included sub-tables. */
102
const vkPoptOption* opt2 = vkFindOption( opt->arg, longFlag, shortFlag, singleDash );
104
continue; /* no match in sub-table */
105
/* found match: return option */
108
else { /* is-a leaf */
109
if ( longFlag && opt->longFlag && !singleDash &&
110
!strcmp( longFlag, opt->longFlag ) )
111
break; /* longFlag match */
112
if (shortFlag && shortFlag == opt->shortFlag)
113
break; /* shortFlag match */
117
if ( !opt->longFlag && !opt->shortFlag )
118
return NULL; /* end of optArr: no match found */
125
static const char * vkExpandNextArg( const char * s )
128
size_t tn = strlen(s) + 1;
131
te = t = (char*)malloc(tn);;
134
while ((c = *s++) != '\0') {
139
/* memory leak, hard to plug */
140
t = (char*)realloc(t, strlen(t) + 1);
145
/* get next option opt_ret
146
returns 0 on success, 1 on last item, PERROR_* on error */
147
int vkPoptGetNextOpt( vkPoptContext con,
148
char *arg_val/*OUT*/,
149
const vkPoptOption** opt_ret/*OUT*/ )
151
const vkPoptOption * opt = NULL;
159
const char * origOptString = NULL;
160
const char * longArg = NULL;
163
while ( !con->os->nextCharArg &&
164
con->os->next == con->os->argc &&
165
con->os > con->optionStack ) {
166
vkCleanOSE( con->os-- );
169
if ( !con->os->nextCharArg &&
170
con->os->next == con->os->argc ) {
174
/* process next long option */
175
if ( !con->os->nextCharArg ) {
176
char * localOptString, * optString;
178
thisopt = con->os->next;
179
if ( con->os->argv != NULL )
180
origOptString = con->os->argv[con->os->next++];
182
if ( origOptString == NULL ) {
183
return PERROR_BADOPT;
185
if ( strcmp(origOptString, "--") == 0 ) {
186
return PERROR_BADQUOTE;
189
if ( con->restLeftover || *origOptString != '-' ) {
190
if ( con->flags & PCONTEXT_POSIXMEHARDER )
191
con->restLeftover = 1;
192
if ( con->leftovers != NULL )
193
con->leftovers[con->numLeftovers++] = origOptString;
197
/* make a copy we can hack at */
198
localOptString = optString =
199
strcpy((char*)alloca(strlen(origOptString) + 1), origOptString);
201
if ( optString[0] == '\0' )
202
return PERROR_BADOPT;
204
if ( optString[1] == '-' && !optString[2] ) {
205
con->restLeftover = 1;
211
if ( *optString == '-' )
212
singleDash = 0, optString++;
216
/* Check for "--long=arg" option. */
217
for ( oe = optString; *oe && *oe != '='; oe++ )
220
/* FIX: don't use '=' for shortopts */
222
return PERROR_NODASH;
224
/* longArg is mapped back to persistent storage. */
225
longArg = origOptString + (oe - localOptString);
226
/* FIX: catch cases where --longarg=<no-arg> */
227
if ( strlen(longArg) == 0 ) {
228
//printf("1: returning PERROR_NOARG\n");
233
else if ( singleDash == 0 ) {
234
/* FIX: catch cases where we didn't find an '=',
235
and this is a --longarg option */
236
//printf("2: returning PERROR_NOARG\n");
241
opt = vkFindOption( con->options, optString,
243
if ( !opt && !singleDash ) {
244
//printf("returning PERROR_BADOPT\n");
245
return PERROR_BADOPT;
250
con->os->nextCharArg = origOptString + 1;
256
/* process next short option */
257
if ( con->os->nextCharArg ) {
258
origOptString = con->os->nextCharArg;
259
con->os->nextCharArg = NULL;
261
opt = vkFindOption( con->options, NULL, *origOptString, 0 );
263
return PERROR_BADOPT;
268
if ( *origOptString != '\0' )
269
con->os->nextCharArg = origOptString;
273
return PERROR_BADOPT;
275
if ( opt->argType != VkOPTION::ARG_NONE ) {
276
con->os->nextArg = (const char*)_free(con->os->nextArg);
278
longArg = vkExpandNextArg( longArg );
279
con->os->nextArg = longArg;
280
} else if ( con->os->nextCharArg ) {
281
longArg = vkExpandNextArg( con->os->nextCharArg);
282
con->os->nextArg = longArg;
283
con->os->nextCharArg = NULL;
285
while ( con->os->next == con->os->argc &&
286
con->os > con->optionStack ) {
287
vkCleanOSE( con->os-- );
289
if ( con->os->next == con->os->argc ) {
290
/* FIX: con->os->argv not defined */
292
con->os->nextArg = NULL;
294
if ( con->os->argv != NULL ) {
295
/* watch out: subtle side-effects live here. */
296
longArg = con->os->argv[con->os->next++];
297
longArg = vkExpandNextArg( longArg );
298
con->os->nextArg = longArg;
304
/* store the argument value for checking */
305
if ( con->os->nextArg ) {
306
sprintf( arg_val, "%s", con->os->nextArg );
308
} /* end if ! VkOPTION::ARG_NONE */
310
if ( opt->optKey >= 0 ) { /* is-a leaf */
314
if ( (con->finalArgvCount + 2) >= (con->finalArgvAlloced) ) {
315
con->finalArgvAlloced += 10;
316
con->finalArgv = (const char**)realloc(con->finalArgv,
317
sizeof(*con->finalArgv) * con->finalArgvAlloced);
320
if ( con->finalArgv != NULL ) {
321
char *s = (char*)malloc((opt->longFlag ? strlen(opt->longFlag) : 0) + 3);
323
if ( opt->longFlag ) {
324
sprintf(s, "--%s", opt->longFlag);
326
sprintf(s, "-%c", opt->shortFlag);
328
con->finalArgv[con->finalArgvCount++] = s;
330
con->finalArgv[con->finalArgvCount++] = NULL;
333
if ((opt->arg == NULL/* leaf */) &&
334
(opt->argType != VkOPTION::ARG_NONE)) {
335
if (con->finalArgv != NULL && con->os->nextArg) {
336
char* s = (char*)malloc( strlen(con->os->nextArg)+1 );
338
vkPrintErr("virtual memory exhausted.");
341
con->finalArgv[con->finalArgvCount++] =
342
strcpy( s, con->os->nextArg );
346
} /* end while ( !done ) */
348
// vk_assert( opt != NULL );
354
vkPoptContext vkPoptFreeContext( vkPoptContext con )
359
/* poptResetContext( con ); */
361
while ( con->os > con->optionStack ) {
362
vkCleanOSE(con->os--);
364
con->os->nextCharArg = NULL;
365
con->os->nextArg = NULL;
366
con->os->next = 1; /* skip argv[0] */
367
con->numLeftovers = 0;
368
con->nextLeftover = 0;
369
con->restLeftover = 0;
371
if ( con->finalArgv != NULL )
372
for (i = 0; i < con->finalArgvCount; i++)
373
con->finalArgv[i] = (const char*)_free(con->finalArgv[i]);
374
con->finalArgvCount = 0;
376
con->leftovers = (const char**)_free( con->leftovers );
377
con->finalArgv = (const char**)_free( con->finalArgv );
379
con = (vkPoptContext)_free( con );
384
const char * vkPoptBadOption( vkPoptContext con )
386
struct optionStackEntry * os = NULL;
389
os = con->optionStack;
391
return ( os && os->argv ? os->argv[os->next - 1] : NULL );
396
/*------ help printing stuff --------------------------------*/
397
/* set in poptPrintHelp */
398
static int leftColWidth = -1;
400
static const char * vkGetHelpDesc( const vkPoptOption * opt )
402
/* if ARG_NONE: no arguments to describe */
403
if ( opt->argType == VkOPTION::ARG_NONE )
407
return opt->helpdesc;
413
/* called from vkTableHelp() for leaf table */
414
static void vkSingleOptionHelp( FILE * fp,
415
const vkPoptOption * opt )
417
int indentLength = leftColWidth + 2;
418
int lineLength = 80 - indentLength;
419
const char * help = opt->helptxt;
420
const char * helpdesc = vkGetHelpDesc( opt );
424
int nb = leftColWidth + 1;
426
/* make sure there's more than enough room in target buffer. */
428
nb += strlen( opt->longFlag );
430
nb += strlen( helpdesc );
432
left = (char*)malloc( nb );
436
left[leftColWidth] = '\0';
438
if ( opt->longFlag && opt->shortFlag ) {
439
sprintf( left, "-%c, --%s", opt->shortFlag, opt->longFlag);
440
} else if (opt->shortFlag != '\0') {
441
sprintf( left, "-%c", opt->shortFlag);
442
} else if (opt->longFlag) {
443
sprintf( left, "--%s", opt->longFlag );
451
char * le = left + strlen( left );
453
strcpy( le, helpdesc );
459
fprintf( fp," %-*s", leftColWidth, left );
461
fprintf( fp," %s\n", left );
465
left = (char*)_free(left);
471
helpLength = strlen( help );
472
while ( helpLength > lineLength ) {
476
ch = help + lineLength - 1;
477
while ( ch > help && !isspace(*ch) )
481
while ( ch > (help + 1) && isspace(*ch) )
485
sprintf( format, "%%.%ds\n%%%ds",
486
(int) (ch - help), indentLength );
487
fprintf( fp, format, help, " " );
489
while ( isspace(*help) && *help )
491
helpLength = strlen( help );
495
fprintf( fp, "%s\n", help );
498
defs = (char*)_free(defs);
499
left = (char*)_free(left);
503
static int vkMaxArgWidth( const vkPoptOption * opt )
512
for (; opt->longFlag || opt->shortFlag || opt->arg; opt++ ) {
514
if ( opt->arg != NULL ) { /* is-a table */
515
/* recurse on included sub-tables. */
516
len = vkMaxArgWidth( opt->arg );
520
else { /* is-a leaf */
521
len = sizeof(" ") - 1;
522
if ( opt->shortFlag != '\0' )
523
len += sizeof("-X")-1;
524
if ( opt->shortFlag != '\0' && opt->longFlag )
525
len += sizeof(", ")-1;
527
len += sizeof("--") - 1;
528
len += strlen( opt->longFlag );
531
s = vkGetHelpDesc( opt );
533
len += sizeof("=") - 1 + strlen( s );
542
/* this version prints nested tables first. swap 'em over if this
543
isn't the behaviour you want. */
544
static void vkTableHelp( FILE * fp, const vkPoptOption * opt )
549
/* recurse over all tables */
550
for (; opt->longFlag || opt->shortFlag || opt->arg; opt++) {
551
if ( opt->arg != NULL ) { /* is-a table */
552
/* print table title */
553
fprintf( fp, "\n%s options:\n", opt->helptxt );
554
/* recurse on included sub-tables. */
555
vkTableHelp( fp, opt->arg );
557
else { /* is-a leaf */
559
vkSingleOptionHelp( fp, opt );
565
/* if tableName == NULL, recurses over all tables;
566
else just prints contents of the specified table. */
567
void vkPoptPrintHelp( vkPoptContext con, FILE * fp,
568
const char * tableName )
572
fprintf( fp, "\nUsage:" );
573
fn = con->optionStack->argv[0];
575
if ( strchr(fn, '/') )
576
fn = strrchr( fn, '/' ) + 1;
577
fprintf( fp, " %s", fn );
581
"[valkyrie-opts] [valgrind-opts] [prog-and-args]\n" );
583
leftColWidth = vkMaxArgWidth( con->options );
585
if ( tableName == NULL ) {
586
/* print all tables */
587
vkTableHelp( fp, con->options );
589
/* trawl through con->options till we find the right table */
590
const vkPoptOption * opt;
591
for ( opt = con->options; (opt != NULL) &&
592
(opt->helptxt != NULL); opt++ ) {
593
if ( strcmp( opt->helptxt, tableName ) == 0 ) {
597
if ( (opt != NULL) && (opt->helptxt) != NULL ) {
599
fprintf( fp, "\n%s options:\n", opt->helptxt );
601
vkPrintErr("Error: vkPoptPrintHelp(): No match found for table '%s'.", tableName);
602
vkTableHelp( fp, opt->arg );