/* * tkArgv.c -- * * This file contains a function that handles table-based argv-argc * parsing. * * Copyright © 1990-1994 The Regents of the University of California. * Copyright © 1994-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkInt.h" /* * Default table of argument descriptors. These are normally available in * every application. */ static const Tk_ArgvInfo defaultTable[] = { {"-help", TK_ARGV_HELP, NULL, NULL, "Print summary of command-line options and abort"}, {NULL, TK_ARGV_END, NULL, NULL, NULL} }; /* * Forward declarations for functions defined in this file: */ static void PrintUsage(Tcl_Interp *interp, const Tk_ArgvInfo *argTable, int flags); /* *---------------------------------------------------------------------- * * Tk_ParseArgv -- * * Process an argv array according to a table of expected command-line * options. See the manual page for more details. * * Results: * The return value is a standard Tcl return value. If an error occurs * then an error message is left in the interp's result. Under normal * conditions, both *argcPtr and *argv are modified to return the * arguments that couldn't be processed here (they didn't match the * option table, or followed an TK_ARGV_REST argument). * * Side effects: * Variables may be modified, resources may be entered for tkwin, or * functions may be called. It all depends on the arguments and their * entries in argTable. See the user documentation for details. * *---------------------------------------------------------------------- */ int Tk_ParseArgv( Tcl_Interp *interp, /* Place to store error message. */ Tk_Window tkwin, /* Window to use for setting Tk options. NULL * means ignore Tk option specs. */ int *argcPtr, /* Number of arguments in argv. Modified to * hold # args left in argv at end. */ const char **argv, /* Array of arguments. Modified to hold those * that couldn't be processed here. */ const Tk_ArgvInfo *argTable, /* Array of option descriptions */ int flags) /* Or'ed combination of various flag bits, * such as TK_ARGV_NO_DEFAULTS. */ { const Tk_ArgvInfo *infoPtr; /* Pointer to the current entry in the table * of argument descriptions. */ const Tk_ArgvInfo *matchPtr;/* Descriptor that matches current argument. */ const char *curArg; /* Current argument */ char c; /* Second character of current arg (used for * quick check for matching; use 2nd char. * because first char. will almost always be * '-'). */ int srcIndex; /* Location from which to read next argument * from argv. */ int dstIndex; /* Index into argv to which next unused * argument should be copied (never greater * than srcIndex). */ int argc; /* # arguments in argv still to process. */ size_t length; /* Number of characters in current argument. */ char *endPtr; /* Used for identifying junk in arguments. */ int i; if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) { srcIndex = dstIndex = 0; argc = *argcPtr; } else { srcIndex = dstIndex = 1; argc = *argcPtr-1; } while (argc > 0) { curArg = argv[srcIndex]; srcIndex++; argc--; length = strlen(curArg); if (length > 0) { c = curArg[1]; } else { c = 0; } /* * Loop throught the argument descriptors searching for one with the * matching key string. If found, leave a pointer to it in matchPtr. */ matchPtr = NULL; for (i = 0; i < 2; i++) { if (i == 0) { infoPtr = argTable; } else { infoPtr = defaultTable; } for (; (infoPtr != NULL) && (infoPtr->type != TK_ARGV_END); infoPtr++) { if (infoPtr->key == NULL) { continue; } if ((infoPtr->key[1] != c) || (strncmp(infoPtr->key, curArg, length) != 0)) { continue; } if ((tkwin == NULL) && ((infoPtr->type == TK_ARGV_CONST_OPTION) || (infoPtr->type == TK_ARGV_OPTION_VALUE) || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) { continue; } if (infoPtr->key[length] == 0) { matchPtr = infoPtr; goto gotMatch; } if (flags & TK_ARGV_NO_ABBREV) { continue; } if (matchPtr != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "ambiguous option \"%s\"", curArg)); Tcl_SetErrorCode(interp, "TK", "ARG", "AMBIGUOUS", curArg, NULL); return TCL_ERROR; } matchPtr = infoPtr; } } if (matchPtr == NULL) { /* * Unrecognized argument. Just copy it down, unless the caller * prefers an error to be registered. */ if (flags & TK_ARGV_NO_LEFTOVERS) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "unrecognized argument \"%s\"", curArg)); Tcl_SetErrorCode(interp, "TK", "ARG", "UNRECOGNIZED", curArg, NULL); return TCL_ERROR; } argv[dstIndex] = curArg; dstIndex++; continue; } /* * Take the appropriate action based on the option type */ gotMatch: infoPtr = matchPtr; switch (infoPtr->type) { case TK_ARGV_CONSTANT: *((int *) infoPtr->dst) = (int)PTR2INT(infoPtr->src); break; case TK_ARGV_INT: if (argc == 0) { goto missingArg; } *((int *) infoPtr->dst) = strtol(argv[srcIndex], &endPtr, 0); if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "expected %s argument for \"%s\" but got \"%s\"", "integer", infoPtr->key, argv[srcIndex])); Tcl_SetErrorCode(interp, "TK", "ARG", "INTEGER", curArg,NULL); return TCL_ERROR; } srcIndex++; argc--; break; case TK_ARGV_STRING: if (argc == 0) { goto missingArg; } *((const char **) infoPtr->dst) = argv[srcIndex]; srcIndex++; argc--; break; case TK_ARGV_UID: if (argc == 0) { goto missingArg; } *((Tk_Uid *) infoPtr->dst) = Tk_GetUid(argv[srcIndex]); srcIndex++; argc--; break; case TK_ARGV_REST: *((int *) infoPtr->dst) = dstIndex; goto argsDone; case TK_ARGV_FLOAT: if (argc == 0) { goto missingArg; } *((double *) infoPtr->dst) = strtod(argv[srcIndex], &endPtr); if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "expected %s argument for \"%s\" but got \"%s\"", "floating-point", infoPtr->key, argv[srcIndex])); Tcl_SetErrorCode(interp, "TK", "ARG", "FLOAT", curArg, NULL); return TCL_ERROR; } srcIndex++; argc--; break; case TK_ARGV_FUNC: { typedef int (ArgvFunc)(char *, const char *, const char *); ArgvFunc *handlerProc = (ArgvFunc *)infoPtr->src; if (handlerProc((char *)infoPtr->dst, infoPtr->key, argv[srcIndex])) { srcIndex++; argc--; } break; } case TK_ARGV_GENFUNC: { typedef int (ArgvGenFunc)(char *, Tcl_Interp *, const char *, int, const char **); ArgvGenFunc *handlerProc = (ArgvGenFunc *)infoPtr->src; argc = handlerProc((char *)infoPtr->dst, interp, infoPtr->key, argc, argv+srcIndex); if (argc < 0) { return TCL_ERROR; } break; } case TK_ARGV_HELP: PrintUsage(interp, argTable, flags); Tcl_SetErrorCode(interp, "TK", "ARG", "HELP", NULL); return TCL_ERROR; case TK_ARGV_CONST_OPTION: Tk_AddOption(tkwin, (char *)infoPtr->dst, (char *)infoPtr->src, TK_INTERACTIVE_PRIO); break; case TK_ARGV_OPTION_VALUE: if (argc < 1) { goto missingArg; } Tk_AddOption(tkwin, (char *)infoPtr->dst, argv[srcIndex], TK_INTERACTIVE_PRIO); srcIndex++; argc--; break; case TK_ARGV_OPTION_NAME_VALUE: if (argc < 2) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "\"%s\" option requires two following arguments", curArg)); Tcl_SetErrorCode(interp, "TK", "ARG", "NAME_VALUE", curArg, NULL); return TCL_ERROR; } Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1], TK_INTERACTIVE_PRIO); srcIndex += 2; argc -= 2; break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "bad argument type %d in Tk_ArgvInfo", infoPtr->type)); Tcl_SetErrorCode(interp, "TK", "API_ABUSE", NULL); return TCL_ERROR; } } /* * If we broke out of the loop because of an OPT_REST argument, copy the * remaining arguments down. */ argsDone: while (argc) { argv[dstIndex] = argv[srcIndex]; srcIndex++; dstIndex++; argc--; } argv[dstIndex] = NULL; *argcPtr = dstIndex; return TCL_OK; missingArg: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "\"%s\" option requires an additional argument", curArg)); Tcl_SetErrorCode(interp, "TK", "ARG", "MISSING", curArg, NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * PrintUsage -- * * Generate a help string describing command-line options. * * Results: * The interp's result will be modified to hold a help string describing * all the options in argTable, plus all those in the default table * unless TK_ARGV_NO_DEFAULTS is specified in flags. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PrintUsage( Tcl_Interp *interp, /* Place information in this interp's result * area. */ const Tk_ArgvInfo *argTable,/* Array of command-specific argument * descriptions. */ int flags) /* If the TK_ARGV_NO_DEFAULTS bit is set in * this word, then don't generate information * for default options. */ { const Tk_ArgvInfo *infoPtr; size_t width, i, numSpaces; Tcl_Obj *message; /* * First, compute the width of the widest option key, so that we can make * everything line up. */ width = 4; for (i = 0; i < 2; i++) { for (infoPtr = i ? defaultTable : argTable; infoPtr->type != TK_ARGV_END; infoPtr++) { size_t length; if (infoPtr->key == NULL) { continue; } length = strlen(infoPtr->key); if (length > width) { width = length; } } } message = Tcl_NewStringObj("Command-specific options:", TCL_INDEX_NONE); for (i = 0; ; i++) { for (infoPtr = i ? defaultTable : argTable; infoPtr->type != TK_ARGV_END; infoPtr++) { if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) { Tcl_AppendPrintfToObj(message, "\n%s", infoPtr->help); continue; } Tcl_AppendPrintfToObj(message, "\n %s:", infoPtr->key); numSpaces = width + 1 - strlen(infoPtr->key); while (numSpaces-- > 0) { Tcl_AppendToObj(message, " ", 1); } Tcl_AppendToObj(message, infoPtr->help, TCL_INDEX_NONE); switch (infoPtr->type) { case TK_ARGV_INT: Tcl_AppendPrintfToObj(message, "\n\t\tDefault value: %d", *((int *) infoPtr->dst)); break; case TK_ARGV_FLOAT: Tcl_AppendPrintfToObj(message, "\n\t\tDefault value: %f", *((double *) infoPtr->dst)); break; case TK_ARGV_STRING: { char *string = *((char **) infoPtr->dst); if (string != NULL) { Tcl_AppendPrintfToObj(message, "\n\t\tDefault value: \"%s\"", string); } break; } default: break; } } if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) { break; } Tcl_AppendToObj(message, "\nGeneric options for all commands:", TCL_INDEX_NONE); } Tcl_SetObjResult(interp, message); } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */