1/* 2 * tkArgv.c -- 3 * 4 * This file contains a function that handles table-based argv-argc 5 * parsing. 6 * 7 * Copyright (c) 1990-1994 The Regents of the University of California. 8 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include "tkInt.h" 17 18/* 19 * Default table of argument descriptors. These are normally available in 20 * every application. 21 */ 22 23static Tk_ArgvInfo defaultTable[] = { 24 {"-help", TK_ARGV_HELP, NULL, NULL, 25 "Print summary of command-line options and abort"}, 26 {NULL, TK_ARGV_END, NULL, NULL, NULL} 27}; 28 29/* 30 * Forward declarations for functions defined in this file: 31 */ 32 33static void PrintUsage(Tcl_Interp *interp, Tk_ArgvInfo *argTable, 34 int flags); 35 36/* 37 *---------------------------------------------------------------------- 38 * 39 * Tk_ParseArgv -- 40 * 41 * Process an argv array according to a table of expected command-line 42 * options. See the manual page for more details. 43 * 44 * Results: 45 * The return value is a standard Tcl return value. If an error occurs 46 * then an error message is left in the interp's result. Under normal 47 * conditions, both *argcPtr and *argv are modified to return the 48 * arguments that couldn't be processed here (they didn't match the 49 * option table, or followed an TK_ARGV_REST argument). 50 * 51 * Side effects: 52 * Variables may be modified, resources may be entered for tkwin, or 53 * functions may be called. It all depends on the arguments and their 54 * entries in argTable. See the user documentation for details. 55 * 56 *---------------------------------------------------------------------- 57 */ 58 59int 60Tk_ParseArgv( 61 Tcl_Interp *interp, /* Place to store error message. */ 62 Tk_Window tkwin, /* Window to use for setting Tk options. NULL 63 * means ignore Tk option specs. */ 64 int *argcPtr, /* Number of arguments in argv. Modified to 65 * hold # args left in argv at end. */ 66 CONST char **argv, /* Array of arguments. Modified to hold those 67 * that couldn't be processed here. */ 68 Tk_ArgvInfo *argTable, /* Array of option descriptions */ 69 int flags) /* Or'ed combination of various flag bits, 70 * such as TK_ARGV_NO_DEFAULTS. */ 71{ 72 register Tk_ArgvInfo *infoPtr; 73 /* Pointer to the current entry in the table 74 * of argument descriptions. */ 75 Tk_ArgvInfo *matchPtr; /* Descriptor that matches current argument. */ 76 CONST char *curArg; /* Current argument */ 77 register char c; /* Second character of current arg (used for 78 * quick check for matching; use 2nd char. 79 * because first char. will almost always be 80 * '-'). */ 81 int srcIndex; /* Location from which to read next argument 82 * from argv. */ 83 int dstIndex; /* Index into argv to which next unused 84 * argument should be copied (never greater 85 * than srcIndex). */ 86 int argc; /* # arguments in argv still to process. */ 87 size_t length; /* Number of characters in current argument. */ 88 int i; 89 90 if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) { 91 srcIndex = dstIndex = 0; 92 argc = *argcPtr; 93 } else { 94 srcIndex = dstIndex = 1; 95 argc = *argcPtr-1; 96 } 97 98 while (argc > 0) { 99 curArg = argv[srcIndex]; 100 srcIndex++; 101 argc--; 102 length = strlen(curArg); 103 if (length > 0) { 104 c = curArg[1]; 105 } else { 106 c = 0; 107 } 108 109 /* 110 * Loop throught the argument descriptors searching for one with the 111 * matching key string. If found, leave a pointer to it in matchPtr. 112 */ 113 114 matchPtr = NULL; 115 for (i = 0; i < 2; i++) { 116 if (i == 0) { 117 infoPtr = argTable; 118 } else { 119 infoPtr = defaultTable; 120 } 121 for (; (infoPtr != NULL) && (infoPtr->type != TK_ARGV_END); 122 infoPtr++) { 123 if (infoPtr->key == NULL) { 124 continue; 125 } 126 if ((infoPtr->key[1] != c) 127 || (strncmp(infoPtr->key, curArg, length) != 0)) { 128 continue; 129 } 130 if ((tkwin == NULL) 131 && ((infoPtr->type == TK_ARGV_CONST_OPTION) 132 || (infoPtr->type == TK_ARGV_OPTION_VALUE) 133 || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) { 134 continue; 135 } 136 if (infoPtr->key[length] == 0) { 137 matchPtr = infoPtr; 138 goto gotMatch; 139 } 140 if (flags & TK_ARGV_NO_ABBREV) { 141 continue; 142 } 143 if (matchPtr != NULL) { 144 Tcl_AppendResult(interp, "ambiguous option \"", curArg, 145 "\"", NULL); 146 return TCL_ERROR; 147 } 148 matchPtr = infoPtr; 149 } 150 } 151 if (matchPtr == NULL) { 152 /* 153 * Unrecognized argument. Just copy it down, unless the caller 154 * prefers an error to be registered. 155 */ 156 157 if (flags & TK_ARGV_NO_LEFTOVERS) { 158 Tcl_AppendResult(interp, "unrecognized argument \"", 159 curArg, "\"", NULL); 160 return TCL_ERROR; 161 } 162 argv[dstIndex] = curArg; 163 dstIndex++; 164 continue; 165 } 166 167 /* 168 * Take the appropriate action based on the option type 169 */ 170 171 gotMatch: 172 infoPtr = matchPtr; 173 switch (infoPtr->type) { 174 case TK_ARGV_CONSTANT: 175 *((int *) infoPtr->dst) = PTR2INT(infoPtr->src); 176 break; 177 case TK_ARGV_INT: 178 if (argc == 0) { 179 goto missingArg; 180 } else { 181 char *endPtr; 182 183 *((int *) infoPtr->dst) = strtol(argv[srcIndex], &endPtr, 0); 184 if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { 185 Tcl_AppendResult(interp,"expected integer argument for \"", 186 infoPtr->key, "\" but got \"", argv[srcIndex], 187 "\"", NULL); 188 return TCL_ERROR; 189 } 190 srcIndex++; 191 argc--; 192 } 193 break; 194 case TK_ARGV_STRING: 195 if (argc == 0) { 196 goto missingArg; 197 } 198 *((CONST char **)infoPtr->dst) = argv[srcIndex]; 199 srcIndex++; 200 argc--; 201 break; 202 case TK_ARGV_UID: 203 if (argc == 0) { 204 goto missingArg; 205 } 206 *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]); 207 srcIndex++; 208 argc--; 209 break; 210 case TK_ARGV_REST: 211 *((int *) infoPtr->dst) = dstIndex; 212 goto argsDone; 213 case TK_ARGV_FLOAT: 214 if (argc == 0) { 215 goto missingArg; 216 } else { 217 char *endPtr; 218 219 *((double *) infoPtr->dst) = strtod(argv[srcIndex], &endPtr); 220 if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { 221 Tcl_AppendResult(interp, "expected floating-point ", 222 "argument for \"", infoPtr->key, "\" but got \"", 223 argv[srcIndex], "\"", NULL); 224 return TCL_ERROR; 225 } 226 srcIndex++; 227 argc--; 228 } 229 break; 230 case TK_ARGV_FUNC: { 231 typedef int (ArgvFunc)(char *, char *, CONST char *); 232 ArgvFunc *handlerProc = (ArgvFunc *) infoPtr->src; 233 234 if ((*handlerProc)(infoPtr->dst, infoPtr->key, argv[srcIndex])) { 235 srcIndex++; 236 argc--; 237 } 238 break; 239 } 240 case TK_ARGV_GENFUNC: { 241 typedef int (ArgvGenFunc)(char *, Tcl_Interp *, char *, int, 242 CONST char **); 243 ArgvGenFunc *handlerProc = (ArgvGenFunc *) infoPtr->src; 244 245 argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key, 246 argc, argv+srcIndex); 247 if (argc < 0) { 248 return TCL_ERROR; 249 } 250 break; 251 } 252 case TK_ARGV_HELP: 253 PrintUsage(interp, argTable, flags); 254 return TCL_ERROR; 255 case TK_ARGV_CONST_OPTION: 256 Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src, 257 TK_INTERACTIVE_PRIO); 258 break; 259 case TK_ARGV_OPTION_VALUE: 260 if (argc < 1) { 261 goto missingArg; 262 } 263 Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex], 264 TK_INTERACTIVE_PRIO); 265 srcIndex++; 266 argc--; 267 break; 268 case TK_ARGV_OPTION_NAME_VALUE: 269 if (argc < 2) { 270 Tcl_AppendResult(interp, "\"", curArg, 271 "\" option requires two following arguments", NULL); 272 return TCL_ERROR; 273 } 274 Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1], 275 TK_INTERACTIVE_PRIO); 276 srcIndex += 2; 277 argc -= 2; 278 break; 279 default: { 280 char buf[64 + TCL_INTEGER_SPACE]; 281 282 sprintf(buf, "bad argument type %d in Tk_ArgvInfo", infoPtr->type); 283 Tcl_SetResult(interp, buf, TCL_VOLATILE); 284 return TCL_ERROR; 285 } 286 } 287 } 288 289 /* 290 * If we broke out of the loop because of an OPT_REST argument, copy the 291 * remaining arguments down. 292 */ 293 294 argsDone: 295 while (argc) { 296 argv[dstIndex] = argv[srcIndex]; 297 srcIndex++; 298 dstIndex++; 299 argc--; 300 } 301 argv[dstIndex] = NULL; 302 *argcPtr = dstIndex; 303 return TCL_OK; 304 305 missingArg: 306 Tcl_AppendResult(interp, "\"", curArg, 307 "\" option requires an additional argument", NULL); 308 return TCL_ERROR; 309} 310 311/* 312 *---------------------------------------------------------------------- 313 * 314 * PrintUsage -- 315 * 316 * Generate a help string describing command-line options. 317 * 318 * Results: 319 * The interp's result will be modified to hold a help string describing 320 * all the options in argTable, plus all those in the default table 321 * unless TK_ARGV_NO_DEFAULTS is specified in flags. 322 * 323 * Side effects: 324 * None. 325 * 326 *---------------------------------------------------------------------- 327 */ 328 329static void 330PrintUsage( 331 Tcl_Interp *interp, /* Place information in this interp's result 332 * area. */ 333 Tk_ArgvInfo *argTable, /* Array of command-specific argument 334 * descriptions. */ 335 int flags) /* If the TK_ARGV_NO_DEFAULTS bit is set in 336 * this word, then don't generate information 337 * for default options. */ 338{ 339 register Tk_ArgvInfo *infoPtr; 340 size_t width, i, numSpaces; 341 char tmp[TCL_DOUBLE_SPACE]; 342 343 /* 344 * First, compute the width of the widest option key, so that we can make 345 * everything line up. 346 */ 347 348 width = 4; 349 for (i = 0; i < 2; i++) { 350 for (infoPtr = i ? defaultTable : argTable; 351 infoPtr->type != TK_ARGV_END; infoPtr++) { 352 size_t length; 353 if (infoPtr->key == NULL) { 354 continue; 355 } 356 length = strlen(infoPtr->key); 357 if (length > width) { 358 width = length; 359 } 360 } 361 } 362 363 Tcl_AppendResult(interp, "Command-specific options:", NULL); 364 for (i = 0; ; i++) { 365 for (infoPtr = i ? defaultTable : argTable; 366 infoPtr->type != TK_ARGV_END; infoPtr++) { 367 if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) { 368 Tcl_AppendResult(interp, "\n", infoPtr->help, NULL); 369 continue; 370 } 371 Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", NULL); 372 numSpaces = width + 1 - strlen(infoPtr->key); 373 while (numSpaces-- > 0) { 374 Tcl_AppendResult(interp, " ", NULL); 375 } 376 Tcl_AppendResult(interp, infoPtr->help, NULL); 377 switch (infoPtr->type) { 378 case TK_ARGV_INT: 379 sprintf(tmp, "%d", *((int *) infoPtr->dst)); 380 Tcl_AppendResult(interp, "\n\t\tDefault value: ", tmp, NULL); 381 break; 382 case TK_ARGV_FLOAT: 383 Tcl_PrintDouble(NULL, *((double *) infoPtr->dst), tmp); 384 Tcl_AppendResult(interp, "\n\t\tDefault value: ", tmp, NULL); 385 break; 386 case TK_ARGV_STRING: { 387 char *string = *((char **) infoPtr->dst); 388 389 if (string != NULL) { 390 Tcl_AppendResult(interp, "\n\t\tDefault value: \"", string, 391 "\"", NULL); 392 } 393 break; 394 } 395 default: 396 break; 397 } 398 } 399 400 if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) { 401 break; 402 } 403 Tcl_AppendResult(interp, "\nGeneric options for all commands:", NULL); 404 } 405} 406 407/* 408 * Local Variables: 409 * mode: c 410 * c-basic-offset: 4 411 * fill-column: 78 412 * End: 413 */ 414